Hi all!
I’m a new Bubbler with no tech background and could use some expertise if anyone can help:
For the past few years, I have used Airtable as my primary platform to do my work through. Now that I have started learning Bubble to build a custom admin interface, I am debating on transferring my data over so that everything can be consolidated.
However, one of the foundational steps to my workflow is a custom script that someone created for me in Airtable Scripts. I would like to find a way to translate my script from Airscript so that I may use it on other platforms, though I am not opposed to figuring out how to simply trigger my script via a button/API call in my Bubble dashboard either.
I have taught myself some basic programming “stuff”, but can’t seem to figure out how to re-work the input.config (it was created to run for a single row upon button click).
Any help/advice/tips would be greatly appreciated!
(My script is quite complex - but the only part I really need assistance with is changing the “await input.recordAsync” portion to be able to accept the inputs from an external source instead of in Airtable. included below for reference!)
Airtable Script
let twoLettersToNumber = {
"ah": 5,
"ch": 8,
"wh": 16,
"tz": 18,
"sh": 21,
"ta": 22,
"th": 22,
};
let lastLetterToNumber = {
"m": 12,
"p": 12,
};
let letterToNumber = {
"a": 1,
"b": 2,
"c": 11,
"d": 4,
"e": 5,
"f": 18,
"g": 3,
"h": 5,
"i": 10,
"j": 10,
"k": 19,
"l": 12,
"m": 13,
"n": 14,
"o": 6,
"p": 17,
"q": 19,
"r": 20,
"s": 15,
"t": 9,
"u": 6,
"v": 6,
"w": 6,
"x": 15,
"y": 16,
"z": 7,
};
/**
* @param {string} token
* @param {{ ah?: number; ch?: number; wh?: number; tz?: number; sh?: number; ta?: number; th?: number; a?: number; b?: number; c?: number; d?: number; e?: number; f?: number; g?: number; h?: number; i?: number; j?: number; k?: number; l?: number; m?: number; n?: number; o?: number; p?: number; q?: number; r?: number; s?: number; t?: number; u?: number; v?: number; w?: number; x?: number; y?: number; z?: number; }} dict
* @param {number[]} arr
*/
function tryInsert(token, dict, arr) {
if (token in dict) {
arr.push(dict[token]);
return true;
}
return false;
}
/**
* @param {string} name
*/
function nameToNumbers(name) {
var result = [];
for (var current = 0; current < name.length;) {
var token = name.substr(current, 2);
if (token.length == 2) {
if (tryInsert(token, twoLettersToNumber, result)) {
current += 2;
} else {
token = name[current];
if (tryInsert(token, letterToNumber, result)) {
current += 1;
} else {
throw `Invalid character found: ${token}`
}
}
} else {
if (tryInsert(token, lastLetterToNumber, result)) {
current += 1;
} else {
if (tryInsert(token, letterToNumber, result)) {
current += 1;
} else {
throw `Invalid character found: ${token}`
}
}
}
}
return result;
}
/**
* @param {number[]} columns
* @param {number[]} numbers
* @param {number} colIndex
*/
function addToColumns(columns, numbers, colIndex) {
for (let number of numbers) {
columns[colIndex] += number;
colIndex += 1;
colIndex = colIndex % columns.length;
}
return colIndex;
}
/**
* @param {number} number
*/
function sumDigits(number) {
let total = 0;
while (number) {
total += number % 10;
number = Math.floor(number / 10);
}
return total;
}
/**
* @param {number} number
* @param {{ (number: number): boolean }} predicate
*/
function reduceUntil(number, predicate) {
while (predicate(number)) {
number = sumDigits(number);
}
return number;
}
/**
* @param {number} number
*/
let greaterThan22 = number => number > 22;
/**
* @param {number} number
*/
let singleDigit = number => number > 9;
/**
* @param {number} number
* @param {number} firstNumberTotal
* @param {number} secondNumberTotal
*/
function reduce(number, firstNumberTotal, secondNumberTotal) {
number = reduceUntil(number, greaterThan22);
let firstNumber = number;
let secondNumber = reduceUntil(number, singleDigit);
firstNumberTotal += firstNumber;
secondNumberTotal += secondNumber;
return {
columnValue: `${firstNumber}-${secondNumber}`,
fnt: firstNumberTotal,
snt: secondNumberTotal
};
}
function reduceColumns(columns) {
var firstNumberTotal = 0;
var secondNumberTotal = 0;
for (var i = 0; i < columns.length; i++) {
var {
columnValue,
fnt,
snt
} = reduce(columns[i], firstNumberTotal, secondNumberTotal);
columns[i] = columnValue;
firstNumberTotal = fnt;
secondNumberTotal = snt;
}
firstNumberTotal = reduceUntil(firstNumberTotal, greaterThan22);
secondNumberTotal = reduceUntil(secondNumberTotal, singleDigit);
columns.push(`${firstNumberTotal}-${secondNumberTotal}`);
return columns;
}
/**
* @param {string} firstName
* @param {string} middleName
* @param {string} lastName
*/
function computeColumns(firstName, middleName, lastName) {
if (!firstName && !middleName && !lastName) {
throw "At least one of first name, middle name or last name should be given"
}
firstName = firstName ? firstName.toLowerCase() : "";
middleName = middleName ? middleName.toLowerCase() : "";
lastName = lastName ? lastName.toLowerCase() : "";
var columns = [0, 0, 0, 0, 0, 0];
var numbers = nameToNumbers(firstName);
var colIndex = 0;
colIndex = addToColumns(columns, numbers, colIndex);
numbers = nameToNumbers(middleName);
colIndex = addToColumns(columns, numbers, colIndex);
numbers = nameToNumbers(lastName);
colIndex = addToColumns(columns, numbers, colIndex);
return reduceColumns(columns);
}
//--------------------Airtable specific code-----------------
let columnNames = ["PK", "SK", "PT", "ST", "PG", "SG", "SD"];
const config = input.config({
title: 'Numerology Script',
items: [
input.config.table('namesTable', {
label: 'Names table',
description: 'The table in which you track names'
}),
input.config.field('firstNameField', {
label: 'First name field',
parentTable: 'namesTable',
}),
input.config.field('middleNameField', {
label: 'Middle name field',
parentTable: 'namesTable',
}),
input.config.field('lastNameField', {
label: 'Last name field',
parentTable: 'namesTable',
}),
input.config.field('pkField', {
label: 'PK field',
parentTable: 'namesTable',
}),
input.config.field('skField', {
label: 'SK field',
parentTable: 'namesTable',
}),
input.config.field('ptField', {
label: 'PT field',
parentTable: 'namesTable',
}),
input.config.field('stField', {
label: 'ST field',
parentTable: 'namesTable',
}),
input.config.field('pgField', {
label: 'PG field',
parentTable: 'namesTable',
}),
input.config.field('sgField', {
label: 'SG field',
parentTable: 'namesTable',
}),
input.config.field('sdField', {
label: 'SD field',
parentTable: 'namesTable',
}),
input.config.table('linkedTable', {
label: 'Templates table',
description: "The table which the names table's PK, SK, PT, ST, PG, SG and SD fields link to"
}),
input.config.field('linkedTableNameField', {
label: 'Templates table attributes field',
description: 'Templates table field containing the values 13-4, 5-5 etc.',
parentTable: 'linkedTable',
}),
input.config.field('aspectField', {
label: 'Templates table aspect field',
description: 'Templates table field containing the values "Physical Karma", "Spiritual Karma" etc.',
parentTable: 'linkedTable',
}),
]
});
async function createLinkedRecordDict() {
let dict = {};
let query = await config.linkedTable.selectRecordsAsync({
fields: [config.linkedTableNameField.name, config.aspectField.name]
});
for (let r of query.records) {
let name = r.getCellValueAsString(config.linkedTableNameField.name);
let aspect = r.getCellValueAsString(config.aspectField.name);
var key = `${name}${aspect}`;
key = key.trim();
dict[key] = r.id;
}
return dict;
}
let dict = await createLinkedRecordDict();
function findLinkedRecordId(attribute, aspect) {
var key = `${attribute}${aspect}`;
return dict[key];
}
let record = await input.recordAsync('Pick a record', config.namesTable);
if (record) {
let firstName = record.getCellValueAsString(config.firstNameField.name);
let middleName = record.getCellValueAsString(config.middleNameField.name);
let lastName = record.getCellValueAsString(config.lastNameField.name);
let columns = computeColumns(firstName, middleName, lastName);
var error = false;
let pkLinkedRecordId = findLinkedRecordId(columns[0], 'Physical Karma');
if (!pkLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[0]} and aspect Physical Karma in ${config.linkedTable.name} table`);
error = true;
}
let skLinkedRecordId = findLinkedRecordId(columns[1], 'Spiritual Karma');
if (!skLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[1]} and aspect Spiritual Karma in ${config.linkedTable.name} table`);
error = true;
}
let ptLinkedRecordId = findLinkedRecordId(columns[2], 'Physical Talents');
if (!ptLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[2]} and aspect Physical Talents in ${config.linkedTable.name} table`);
error = true;
}
let stLinkedRecordId = findLinkedRecordId(columns[3], 'Spiritual Talents');
if (!stLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[3]} and aspect Spiritual Talents in ${config.linkedTable.name} table`);
error = true;
}
let pgLinkedRecordId = findLinkedRecordId(columns[4], 'Physical Goals');
if (!pgLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[4]} and aspect Physical Goals in ${config.linkedTable.name} table`);
error = true;
}
let sgLinkedRecordId = findLinkedRecordId(columns[5], 'Spiritual Goals');
if (!sgLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[5]} and aspect Spiritual Goals in ${config.linkedTable.name} table`);
error = true;
}
let sdLinkedRecordId = findLinkedRecordId(columns[6], 'Soul Destiny');
if (!sdLinkedRecordId) {
console.error(`Failed to find a record with name ${columns[6]} and aspect Soul Destiny in ${config.linkedTable.name} table`);
error = true;
}
if (!error) {
await config.namesTable.updateRecordAsync(record, {
[config.pkField.name]: [{
id: pkLinkedRecordId
}],
[config.skField.name]: [{
id: skLinkedRecordId
}],
[config.ptField.name]: [{
id: ptLinkedRecordId
}],
[config.stField.name]: [{
id: stLinkedRecordId
}],
[config.pgField.name]: [{
id: pgLinkedRecordId
}],
[config.sgField.name]: [{
id: sgLinkedRecordId
}],
[config.sdField.name]: [{
id: sdLinkedRecordId
}],
});
output.text(`Updated columns for ${firstName} ${middleName} ${lastName}`);
}
} else {
output.text('Please pick a record');
}