Tinderbox Forum

Updating one note's attribure value with the attribute value's of another note that has the same name

Let’s say I have a number of notes SS01, SS02, SS03, and so on. And, in these notes, there are attributes A0, A1, A2, that have values in them. Now, let’s say I want to import a spreadsheet that will create new notes with the same names, SS01, SS03, SS03, but the values of A0, A1, and A2 are different. What I want to be able to do is apply a stamp or agent action that will update the values of the attributes of the original notes with the attribute values of the important spreadsheet’s notes. In other words, I’d like to find a way to say if a note has the same name as another note, then I want the older note’s attribute values to be updated with the values from the newer note.

Anyone have any idea how you’d do this?

Do the notes have the same name but different paths? It looks like they don’t.

If not, I’d do the dismambiguation in the spreadsheet.

Yes, the notes have different paths. Here is an example:
TBX L - Simple Gradebook Part 2.tbx (317.5 KB)

I want to update the notes’ attributes in Class Method 1B with the values of the of attributes of the notes found in Sample Assgn scores[].csv.

I could export the Class Method 1B notes and try to merge the notes external, but I’d prefer not to as the notes may have text and children that I don’t want to mess with. I’d prefer to just get their attributes updated.

[EDIT: The code below runs but on closer testing doesn’t actually work - attributes get no value of the wrong one. Unlear as to why. Still, mention this in case i mislead later readers]

Try this stamp on “Class Method 1b”:

var vSrcLocs(collect(find(inside("/Sample Assgn scores[].csv/")),$Path));
var vCurLocs(collect(children,$Path));
vCurLocs.each(aPath){
	vSrcLocs.each(newPath){
		$A01(aPath) = $A01(newPath);
		$A02(aPath) = $A02(newPath);
		$A03(aPath) = $A03(newPath);
		$A04(aPath) = $A04(newPath);
		$A05(aPath) = $A05(newPath);
	}
};

There is scope for further parameterisation, but as has been discussed, it is better to get the basics working before parameterising the whole thing.

I tried parameterising the attribute list but no joy. Building from the above, I adjusted the loop variable names to make the process clearer, with the ‘old’ path being the location being updated and the ‘new’ path the newly added data.

var vSrcLocs(collect(find(inside("/Sample Assgn scores[].csv/")),$Path));
var vCurLocs(collect(children,$Path));
vCurLocs.each(oldPath){
	vSrcLocs.each(newPath){
		$A01(oldPath) = $A01(newPath);
		$A02(oldPath) = $A02(newPath);
		$A03(oldPath) = $A03(newPath);
		$A04(oldPath) = $A04(newPath);
		$A05(oldPath) = $A05(newPath);
	}
};

I got to:

var vSrcLocs(collect(find(inside("/Sample Assgn scores[].csv/")),$Path));
var vCurLocs(collect(children,$Path));
var vAttrList("A01;A02;A03;A04;A05");
vCurLocs.each(oldPath){
	vSrcLocs.each(newPath){
		vAttrList.each(anAttr){
			var vNew( "$" + anAttr + "(" + newPath+")" );
			var vOld( "$" + anAttr + "(" + oldPath+")" );
			$Text = $Text + "\n" + vOld + " = " + vNew;
		}
	}
};

And the text shows a strings like"

$A01(/Sample Assgn scores[].csv/SS001) = $A01(/Class Method 1b/SS001)

The offsets are full paths so I’m not quoting the offset reference string. So, in theory replacing the $Text line above with this:

vOld = vNew; 

I’ve tried compiling the two variables without the ‘$’ prefix and trying this:

$(vOld) = $(vNew); 

Anyway nothing works after most of the afternoon trying. This is usually where I think eval() ought to help, but it doesn’t. Tinderbox appears to be unable to make an attribute reference, on the fly, from an attribute name (with offset path reference) created using lop variable strings. But I’m sue I recall doing something to this ages ago. @eastgate, what have I overlooked.

Essentially, if I read the runes correctly we are doing a merge, and now we have Tinderbox AppleScript we might do this matching/update more easily in that method rather than Action code. in the latter we have less direct control over variables, scope of evaluation and data type assertions.

Did this work for you? DId not for me. Must be doing something wrong. Hop on a zoom?

If I follow you, the text looks right. Correct? If so, I’d take the text, and split it at \n.
Then, I’d eval each line.

If that doesn’t work, I’d set up a test case with very simple paths and short lists.


$A01(/Sample Assgn scores[].csv/SS001) = $A01(/Class Method 1b/SS001)

I’m worried about the square brackets in this example. They might cause trouble. If they do cause trouble, enclosing the path in quotes might conceivably help.

I’ll dig further. The strings I quoted are for debug. They prove that I can create a string like for example:

$A01(/Sample Assgn scores[].csv/SS001) = $A01(/Class Method 1b/SS001);

Indeed the problem is state though I can manufacture, in-loop the right code I can’t make Tinderbox treat it as action code. In this context, eval() seems too strong/deep as it resolves to a true/false whereas I want a command/method someCommand(inputCode) where someCommand resolves any string concatenation with its parentheses and runs the resulting string as action code.

The other part - re $-refs and variables is a manner of making $SomeName from "S"+loopVar. Again I can make the string that as text looks like an attribute reference, I just have no way to tell Tinderbox to treat the string starting with a $-sign as an attribute reference.

I latter made me try a usage $("Some"+"Name"). It doesn’t work but it might be a nice syntax for achieving a reference on the fly to a desired attribute name that is not hard-coded in the code.

Meanwhile, underneath this is another case of people wanting to merge. I’m coming to the conclusion this might better be done via AppleScript if we can figure that out.

eval(…) evaluates an expression and returns its result. Perhaps you want action($MyString).

Though I believe, if you evaluate $Color="red", the action is performed as a side-effect.

1 Like

Here’s an AppleScript solution that may help. Lightly tested, but it seems to be doing the right thing. It also lists names of notes and attributes it couldn’t find in the container of imported notes, in case you want that to double-check those. Have the Tinderbox document open and frontmost, and run.

set oldContainer to "/Class Method 1b/"
set importContainer to "/Sample Assgn scores[].csv/"
set attribsToUpdate to {"A01", "A02", "A03", "A04", "A05"} -- do NOT use $ prefix here

set text item delimiters to ";" -- the Tinderbox separator for lists and sets
set doubleCheck to {} -- track possible update omissions

set tbCodeToListOldNoteNames to "collect(find(inside(" & oldContainer & ")),$Name)"
set tbCodeToListImpNoteNames to "collect(find(inside(" & importContainer & ")),$Name)"

tell front document of application "Tinderbox 8"
	
	set oldNotesAScrList to text items of (evaluate with tbCodeToListOldNoteNames)
	set impNotesAScrList to text items of (evaluate with tbCodeToListImpNoteNames)
	
	repeat with aNoteName in oldNotesAScrList
		
		-- the "it" refers to front document
		set oldNote to find note in it with path oldContainer & aNoteName
		set impNote to find note in it with path importContainer & aNoteName
		
		repeat with anAttrib in attribsToUpdate
			try
				set value of attribute anAttrib of oldNote to value of attribute anAttrib of impNote
			on error
				set end of doubleCheck to (aNoteName as text) & "-$" & (anAttrib as text)
			end try
		end repeat
		
	end repeat
	
	repeat with aName in impNotesAScrList
		tell aName to if it is not in oldNotesAScrList then set end of doubleCheck to (it as text) & "-imp"
	end repeat
	
	display dialog "Doublecheck: " & (doubleCheck as text)
	
end tell

EDIT - added error check for imported notes that are not in container of old notes

2 Likes

Sweet! This works perfectly. :slight_smile: Nicely done. I’ve tested adding new attributes. Works.

Not sure what you meam

Lists where?

If, for example, you are short a note in the container of imported notes, the script won’t be able to find the like-named note there. It will continue, and at the end display a reminder dialog box so you can double-check if that is what you wanted to do.

This is a form of very minimal error checking that illustrates an advantage of using AppleScript in conjunction with Tinderbox action code.

The whole thing is a crazy-looking mélange of code, but can be very powerful and convenient.

1 Like

Oh, I see:

The reason why I did not get it at first is that I added the “extra” student in the export template, not the TBX file. So, this works for error checking the original container in TBX, but it won’t find new notes in the imported spreadsheet. Makes sense.

:slight_smile: I’m looking forward to being able to write this kind of code, I.e. AppleScript, one day, or at least edit it. 5 months ago this as completely unintelligible to me. Thanks to the entire Tinderbox community I can at least following it now and it doesn’t freak me out to add it to my scripts folder and run it. :pray:

1 Like

@satikusala I’m looking forward to being able to write this kind of code, I.e. AppleScript, one day, or at least edit it.

AppleScript, which I read somewhere grew out of HyperCard, seems to frustrate most coding purists. It sure can be ugly and verbose, full of little gotchas. But it’s forgiving, useful for those of us who don’t really know what we are doing but want to “roll our own” extensions to capabilities of an app or pass data between apps, without hiring an expert or going to coding class. I marvel at its ability to access both Tinderbox code and the command line in the same script. Because it can be so useful I suspect it’s going to be around for a while despite years of predictions of its demise. So learning a little is not a complete waste of time.

I’ve updated the script above with a little more error-checking, for imported notes that are not in the container of old notes.

Also, if expending more than trivial time on AppleScripting and or using it for actual work, don’t overlook the excellent AppleScript Debugger.

Love it! New script works great.