Dec 21, 2021 01:44 PM
Hi! I have a sort of complicated situation and I’ve been able to figure out what i need to do, but not the way to actually accomplish it.
I have a base with three tables in it:
Jotform (A) | Supplies (B) | Split Requests (C)
Table A is bringing in responses, Table B is a list of every unique item that has been requested in the jotform, and Table C is where I will split up requests with multiple items in Table A to separate ones.
In the Jotform, people can select multiple items in one form submission. even though someone might send in one form submission, that submission might include 50+ items. The jotform takes the info for each item (data points like item,qty,notes) and separates them by comma, and each item by a line break. that’s my way in with parsing out the data.
I need to take a cell that might say:
Apples,50,red apples
Apples,49,granny smith
Peaches,23
Oranges,44
and split that into separate requests in Table C. then i need to link that record back to the original record in Table A that it came from.
I also need to then create a linked record that matches the name of the item (the first datapoint) and connect that to the corresponding item in Table B.
I also need the script to check if the split item in Table C is already in Table B or not. if it is in that table, link it. if it isn’t, create a new record in that table, pull in the text from the record in Table C to name it, and then link that record to theTable C record.
in summary, im parsing out a list of data in Table A into separate records in table C (that link back to the record they came from in Table A), and then im linking those parsed records to the list of items in Table B (and if i need to create a new record in order to link them, then do that as well).
I’d prefer to have this run from a button in Table A, so once a jotform comes in, i can review it, make sure it’s good to go, and then click the button to run the script. Any help would be greatly appreciated!
Solved! Go to Solution.
Jan 03, 2022 06:29 PM
It’s finished! Here’s the working script, factoring in values from two fields in Table A, matching or creating a record in Table B, and then splitting the request and linking everything in Table C. A cherry on top, I have a field in Table A where I keep track of which records I’ve pressed the button.
Final questions:
const tA = base.getTable('Supply Request Jotform');
const tA_General = tA.getField('General Supplies');
const tA_Other = tA.getField('Other Supplies');
const tB = base.getTable('Supplies');
const tC = base.getTable('Split Requests');
// Button press pulls in that record instead!
let tA_Record = await input.recordAsync('Select a record',tA);
let tA_Gen_Text = tA_Record.getCellValue(tA_General);
let tA_Oth_Text = tA_Record.getCellValue(tA_Other);
function isBlank(str) {
return (!str || /^\s*$/.test(str));
};
if (isBlank(tA_Gen_Text)) {}
else {
let genItemCount = tA_Gen_Text.split("\n").length;
for (let i = 0; i < genItemCount; i++) {
let array = tA_Gen_Text.split("\n");
let line = array[0+i];
let [tA_Item,tA_Qty,tA_Notes] = line.split(',');
/*
output.markdown(
`# General Item #${i+1}:
Item: ${tA_Item}
Qty: ${tA_Qty}
Notes: ${tA_Notes}`);
*/
let tB_Query = await tB.selectRecordsAsync({fields:["Item"]});
let matched = tB_Query.records.filter(item => {return item.name === tA_Item});
let tB_Record;
// If a matching record exists, use it; otherwise make a new record
if (matched.length)
tB_Record = matched[0].id;
else
tB_Record = await tB.createRecordAsync({
"Item": tA_Item,
"Type": {name: 'Other (Custom) Supply'}
});
await tC.createRecordAsync({
"Linked Submission ID": [{id: tA_Record.id}],
"Linked Supply Item": [{id: tB_Record}],
"Qty":parseInt(tA_Qty, 10),
"Notes":tA_Notes
});
};
};
if (isBlank(tA_Oth_Text)) {}
else {
let othItemCount = tA_Oth_Text.split("\n").length;
for (let i = 0; i < othItemCount; i++) {
let array = tA_Oth_Text.split("\n");
let line = array[0+i];
let [tA_Item,tA_Qty,tA_URL,tA_Cost,tA_Notes] = line.split(',');
/*
output.markdown(
`# Other Item #${i+1}:
Item: ${tA_Item}
Qty: ${tA_Qty}
URL: ${tA_URL}
Cost Per: ${tA_Cost}
Notes: ${tA_Notes}`);
*/
let tB_Query = await tB.selectRecordsAsync({fields:["Item"]});
let matched = tB_Query.records.filter(item => {return item.name === tA_Item});
let tB_Record;
// If a matching record exists, use it; otherwise make a new record
if (matched.length)
tB_Record = matched[0].id;
else
tB_Record = await tB.createRecordAsync({
"Item": tA_Item,
"Type": {name: 'Other (Custom) Supply'},
"URL":tA_URL,
"Cost Per":parseInt(tA_Cost, 10),
"Notes":tA_Notes
});
await tC.createRecordAsync({
"Linked Submission ID": [{id: tA_Record.id}],
"Linked Supply Item": [{id: tB_Record}],
"Qty":parseInt(tA_Qty, 10),
"Notes":tA_Notes
});
};
};
await tA.updateRecordAsync(tA_Record, {"Already Split?":{name: 'Yes'}});
Thanks for the help @Justin_Barrett and @Dominik_Bosnjak !
Dec 22, 2021 05:23 PM
Further thinking on this, I’m trying to “storyboard” together what needs to happen, this is what I’m thinking:
Button in Table A, linked to script that pulls info with ASync.
There are going to be more steps in my actual script, because i do this for two different fields in Table A, but if I know how to do one, I’ll be able to duplicate the steps for the other.
Dec 22, 2021 06:40 PM
Nice work breaking down the problem, Jake! I might take a different approach to some of the things that you describe (#1, for instance, can be done with the split()
string method; no regex required), but that’s just because I’ve written lots of code by now. :slightly_smiling_face:
I recommend taking your outline, simplifying the descriptions a bit, and creating comment lines in an empty script as a starting point. Then just start working it from the top down, writing and testing the code for each commented section as you go. Make sure that the script does what you want in each section before moving on to the next one. If you write too much code before you begin testing, it can sometimes be harder to find problems.
If you design the script in a more modular fashion, you could probably do write the core code once as a function, then call that function for each field. That’s a slightly oversimplified description, but the point is that the more reusable you make the code, the more versatile it becomes, and the easier it is to maintain.
Dec 22, 2021 06:47 PM
Thanks! I’m working through it now. Here’s what I’ve got so far, working my way down, right now just trying to parse out the lines in the textbox and see if i can view it split in markdown before going to step two. i also made variables for all of the fields i know im going to use in the script to prep. for #1, i actually am using a split() but it has regex as the identifier inside of it. is that the simpler way you were thinking of? or is there another way to use split to break up each line of text?
here’s the code so far:
const tA = base.getTable('Supply Request Jotform');
let tA_General = tA.getField('General Supplies');
let tA_Other = tA.getField('Other Supplies');
const tB = base.getTable('Supplies');
let tB_Item = tB.getField('Item');
let tB_Type = tB.getField('Type');
let tB_URL = tB.getField('URL (Other)');
let tB_Notes = tB.getField('Notes');
const tC = base.getTable('Split Requests');
let tC_Submission = tC.getField('Linked Submission ID');
let tC_Item = tC.getField('Linked Supply Item');
let tC_Qty = tC.getField('Qty');
let tC_Notes = tC.getField('Notes');
// Button press pulls in that record instead!
let tA_Record = await input.recordAsync('Select a record',tA);
let tA_RecordValue = tA_Record.getCellValue(tA_General);
//let tA_Gen_Line = tA_RecordValue.split(/\r?\n/);
//let [tA_Gen_Item,tA_Gen_Qty,tA_Gen_Notes] = tA_RecordValue.split(',');
//tA_Gen_Line.forEach(dataDisplay);
for (let i = 0; i < tA_RecordValue.split(/\r?\n/).length; i++) {
let [tA_Gen_Item,tA_Gen_Qty,tA_Gen_Notes] = tA_RecordValue.split(',');
output.markdown(`# You have selected
${tA_RecordValue}.
Item: ${tA_Gen_Item}
Qty: ${tA_Gen_Qty}
Notes: ${tA_Gen_Notes}`);
}
function dataDisplay (item, index, array) {
}
It doesn’t totally work. I don’t get errors (yay!) but i also don’t get the text to display the way it’s supposed to. Looking to see what is wonky, but I’m sure it’s in the last few lines of code, seeing as almost all of what I’ve got there are variable declarations
Dec 22, 2021 06:53 PM
From my experience, this is overkill. You can get cell values without first making variables for each field. Just pass the field name:
let tA_RecordValue = tA_Record.getCellValue('General Supplies');
These days it’s extremely unlikely that you’ll get carriage returns (\r
). I would go with this:
for (let i = 0; i < tA_RecordValue.split("\n").length; i++) {
Those are my initial thoughts. I’ve gotta step away to take care of some other stuff, but I might circle back later if I have time.
Dec 22, 2021 06:54 PM
thanks! i’ll keep working on it.
Dec 22, 2021 07:38 PM
Update – I got past the parsing hurdle! I can now click the button in Table A and get a markdown readout.
Extension Cord (specify length in notes),2,12+ feet
Power Strip,4
const tA = base.getTable('Supply Request Jotform');
let tA_General = tA.getField('General Supplies');
let tA_Other = tA.getField('Other Supplies');
const tB = base.getTable('Supplies');
let tB_Item = tB.getField('Item');
let tB_Type = tB.getField('Type');
let tB_URL = tB.getField('URL (Other)');
let tB_Notes = tB.getField('Notes');
const tC = base.getTable('Split Requests');
let tC_Submission = tC.getField('Linked Submission ID');
let tC_Item = tC.getField('Linked Supply Item');
let tC_Qty = tC.getField('Qty');
let tC_Notes = tC.getField('Notes');
// Button press pulls in that record instead!
let tA_Record = await input.recordAsync('Select a record',tA);
let tA_RecordValue = tA_Record.getCellValue(tA_General);
let itemCount = tA_RecordValue.split("\n").length;
output.markdown(`# # of Records in this text box: ${itemCount}`);
for (let i = 0; i < itemCount; i++) {
let array = tA_RecordValue.split("\n");
let line = array[0+i];
let [tA_Gen_Item,tA_Gen_Qty,tA_Gen_Notes] = line.split(',');
output.markdown(
`# Item #${i+1}:
Item: ${tA_Gen_Item}
Qty: ${tA_Gen_Qty}
Notes: ${tA_Gen_Notes}`);
}
going forward, i wont make all those variables, but since i already made these, i figured i might as well keep em. good to know for the future though, thanks!
i looked through Tables B and C and realized i need to slightly adjust my code flow –
next i need to take the data ive parsed out and match the item up with a record in Table B. if the record exists and shares the same value in the notes column, then link that record. if there isn’t a record with matching title and notes, i need to create a new one.
then, i can create a record in table c where it’s linked to the submission in Table A and to the item in Table B. it also needs to input the qty from Table A into the new record in Table C.
Dec 23, 2021 09:53 PM
Sounds like a good plan! :thumbs_up:
Dec 29, 2021 02:23 AM
Kudos on the initiative, maybe consider functional syntax like the Map.prototype.forEach method you mentioned to make your life and prototyping a lot easier - try to write what you want to achieve, not the details on how it needs to happen like going above and beyond to reference your fields as objects.
Don’t use the let keyword unless you’re specifically looking to mutate data, which you mostly aren’t, just creating new records. Mutation leads to side effects, usually bugs that get tougher to predict the more you keep doing it. And you definitely need a simpler method of referencing your fields. The built-in record.getCellValue method doesn’t require you to pass in an actual field object, even though that works. Better to use shorter field names. Or maybe a config of references the script loads separately from another record.
A lot of your setup seems to be reliant on your good judgment, automating that can feel overwhelming at times because computers suck at adapting, which is all the more reason to go functional or at least procedural. Take markdown output, for instance, why not just grab the cell values and use something like JSON.stringify to do figure out what to do? You can pass arguments like
output.markdown('>> ' + JSON.stringify(someString, null, '\n\t'))
To get the codeblock look you’re going for and deal with indentation handling.
Jan 03, 2022 04:56 PM
I’m stuck again and scratching my head. I am trying to match up the name of the item from Table A with an item in Table B, and if it can’t, then it creates a new record in table B with the values from the aprsed out info in Table A. I found a thread with a script from @Justin_Barrett but I’m having trouble putting it into my script. How do I match up a value “Item Name” to a record in Table B?