Save the date! Join us on October 16 for our Product Ops launch event. Register here.
Mar 02, 2020 08:20 PM
Um, yeah - it is. But first, a little of the backstory.
As is often the case just when you’ve thought you’ve seen it all, a client asks -
Is it possible to chain blocks and run them sequentially - as one completes, the next one starts?
No, right?
But then she tossed in another requirement -
The computational results from the first block must be available to the next block and sometimes to all subsequent blocks.
Oy! Nothing like a mind-bender for a Monday morning.
This business requirement is daunting – indeed, impossible given the nature of Script Blocks. I’m sure the Airtable folks have pondered this, but as it stands, it’s off the table for now, right? Indeed, Script Blocks are simply not capable … until and unless … you factor in the amazing power of that which Airtable is known and most famous for - simple data/content management with an intimate relationship with integrated script.
Often, it is the alchemy of data and code that spark the most remote possibilities into a new reality.
Simple Workflow Example - a Happiness Survey
Imagine a three-step process asking basic questions about your day. It’s a survey per-se and I’ve embodied the essence of it into a simple table shown below. Each step has a question and a column for answers. Lastly, there’s a final step - Happiness Assessment - that computes your relative happiness based on your answers
The following screenshot shows this three-step process but also displays a very important field showing Block Script in a long text field.
As I mentioned, each step is a question, and each question is presented abstractly in the script using the value in the first column (i.e., Process Step). The final stage in the process is based upon the answers at each step in the survey process.
BlockChaining
To do this elegantly and well, chaining together different script blocks is ideal.
Each script in each process is loaded and executed sequentially. A single “runner” Block orchestrates the execution of each data-based block and it manages the in-memory scope of the results from start to finish.
The final step includes the block shown below which displays the resulting happiness assessment. Note the reference to aRecords
- this is the variable that contains all of the answers gathered from each of the chained blocks.
And the outcome in the block runner reveals this display when all four steps have completed.
Summary
Block chaining is possible. In and of itself, this is an important realization. While my example is super simple and somewhat lame, imagine the incredible solutions that could be built by chaining block scripts together in easily-managed table data.
As I mentioned, it is also possible to manage, share, and aggregate computational results that occur across multiple block segments.
Tell me - is Block Chaining a thing?
Jun 23, 2020 02:20 AM
In French which is my mother language I would answer with the most accurate expressivity that yes! certainly yes! :star_struck: I hope I can communicate this in English! What does your datascience sentiment analysis application think about it ? :winking_face:
Before I called you, I had examined this (aRecords) myself, like this:
// moving forward? (update the answer to the table)
//
if (outcome != "Back") {
// update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecords[i].id, {
[targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecords[i]["outcome"] = outcome;
//
output.markdown(`** aRecords[i]["outcome"] = outcome; **`);
console.log(aRecords[i]["outcome"]);
//
BreakInput = await input.buttonsAsync("ONE STEP BEYOND ?", ['Yes']);
//
}
to get it STEP by STEP.
However, when I examine ages in the life of aRecords throughout your Script:
aRecords’s life is beginning like this:
var queryResults = await sourceTable.selectRecordsAsync();
var aRecords = queryResults.records;
which seems to me the most normal in Script-Block API.
in
because I didn’t know
})(aRecords)
formalism to push result to array in the context of your Script.
this step
// update the array with the outcome
aRecords[i][“outcome”] = outcome;
that becames friend of mine.
Best,
olπ
Jun 23, 2020 05:13 AM
Your English is almost as good as some English people I’ve met and certainly better than some Americans. :slightly_smiling_face: And despite my last name, you do not want to hear a single utterance in French from me.
One of the design goals of this “sample/example” script was to determine how late script could be bound in a Script Block. That was my only objective in building this - I wanted to see and understand at an architectural level how abstract code could be made to run in a script block or even a custom block.
And, of course, the answer is that it may be bound extremely late - script “injection” into a running framework is entirely within the scope of apps built in Script Block and it demonstrates “just-in-time” application behaviours.
Using this approach, very large script “libraries” (perhaps not built in Airtable but certainly) managed in Airtable could be deployed to avoid the Script Block size ceiling. It also means that new scripts and script versions could be deployed without users copying and pasting into each their own blocks.
A secondary design goal was to see how well the vast benefits of tables could be used to create a smart system, or at least some simple wizards and surveys that compile answers.
Jun 23, 2020 08:47 PM
Thank you very much @Bill.French, really !
In the meantime I’ve come to understand :grinning_face_with_smiling_eyes:
You did add an “outcome” “property (?)” to aRecords[i]
// update the array with the outcome
aRecords[i]["outcome"] = outcome;
which isn’t a “ghost” additional FIELD anyway
that I will never could read through a
getCellValueAsString("outcome")
but well through
let aRecordsOutcome = aRecords[i].outcome
console.log(aRecordsOutcome)
:grinning_face_with_smiling_eyes:
Right now, but I still really don’t understand :
and I wish to know:
EXAMPLE from your Code:
for (var i = 0; i < aRecords.length; i++)
{
if (aRecords[i].getCellValueAsString("Status") == "Active") {
aRecords[i] is not an iterable object.
I wish it would.
How should I add this , if it was possible ?
Contextual AddOn to DOCUMENTATION:
aRecords[i] Properties and Methods:
1. 0: "id"
2. 1: "name"
3. 2: "__parentTable"
4. 3: "_cellValuesByFieldId"
5. 4: "_stringCellValuesByFieldId"
6. 5: "outcome"
7. 6: "constructor"
8. 7: "getCellValue"
9. 8: "getCellValueAsString"
10. 9: "constructor"
11. 10: "__defineGetter__"
12. 11: "__defineSetter__"
13. 12: "hasOwnProperty"
14. 13: "__lookupGetter__"
15. 14: "__lookupSetter__"
16. 15: "isPrototypeOf"
17. 16: "propertyIsEnumerable"
18. 17: "toString"
19. 18: "valueOf"
20. 19: "__proto__"
21. 20: "toLocaleString"
and finally,
This raises a lot of questions on my side as well. Thank you for having developed a little bit this broad and stimulating subject.
Best,
olπ
Jun 24, 2020 05:29 PM
Hi,
In the current state of my knowledge and subject to any hidden bias in my experimental process:
[“outcome”] is a KEY added to aRecords[i]
outcome is a VALUE.
It’s a KEY:VALUE pair added to aRecords[i]
got from:
let keysArray = Object.keys(aRecords[i]);
let valuesArray = Object.values(aRecords[i]);
//
output.text(`let keysArray = Object.keys(aRecords[i]);`);
console.log(keysArray)
output.text(`let valuesArray = Object.values(aRecords[i]);`);
console.log(valuesArray)
but we can get outcome VALUE from
Best,
oLπ
Jun 24, 2020 05:33 PM
Correct, and it is populated via a call back from the eval() call I believe.
Jun 24, 2020 05:41 PM
Thanks @Bill.French !
I could believe it too, so why did you execute the following code ?
Does a call back from the eval() call not be enough ?
Best,
olπ
Jun 24, 2020 05:55 PM
I did try following:
//
// process the block
//
aRecords[i]["outcome"] = null;
//
var outcome = await eval(thisCode);
//
&
// update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecords[i].id, {
[targetOutcomeCol]: outcome,
})
// update the array with the outcome
// aRecords[i]["outcome"] = outcome;
//
Result: “outcome” : outcome is null anyway.
Subject to any hidden bias in my experimental process,
it seems that it is not populated via a call back from the eval() call.
oLπ
Jun 24, 2020 06:42 PM
Correct; the callback only passes the variable at the time of the action (a question was answered, nothing more) to the eval’d script fragment so that it can be used by the fragment if needed. Then we have to embellish that node with what was actually selected.
Jun 24, 2020 06:50 PM
Jun 25, 2020 10:42 PM
Hi @Bill.French,
Sorry to ask you again, to take your time, but as soon as I don’t work on Color Grading of films, I’ll come back to your Script:
exact way of working in the context of this EVAL.
As your script is especially rich for a javascript learner like me, I think I already analyzed it well with Console.log or with output.inspect as well as by dissecting as much as possible of the object aRecords along his content’s dynamic changes but I didn’t understand this point in particular.
Best,
olπ