Foundry Slate: Try to update variable based on a SQL request triggered by an event - palantir-foundry

I try updating a variable on dropdown changing. When the user change the value, an SQL request is executed, then the result must update a variable. It doesn't work it's must be a synchronization issue. But I don't know how to figure it out.
Here's my code
Event code to update {{v_date}} variable
console.log("1")
const maxDate={{f_maxDate}}
console.log("4")
return maxDate
Function code: f_maxDate
const date = {{v_date}}
console.log("2")
const max = {{q_maxDate}}//call of my sql request
console.log("3")
date.value = max.maxDate[0];
return date
In my console, I should have 1 2 3 4, but I have 1 4 2 3. The variable "maxDate" is always empty.
Thanks for helping

Related

Google Apps Script for form sets stuck at first value entered

I am writing a form to update an inventory whenever an item is ordered. In this project, there is a google sheet which is linked to a form. The form contains a script that gets the current inventory from the sheet and subtracts one from this upon order.
The form has three questions: name, size, and comment.
The issue I am getting is that the system works once, and then somehow stores the first response for each successive form submission.
For a minimal working example: I've distilled the issue to the following code, which is triggered by a form submission:
function updateStock()
{
var customer_name = "";
const ss_id = ### I enter spreadsheet ID here ###
const form_id = ### I enter form ID here ###
var sheet = SpreadsheetApp.openById(ss_id);
var form = FormApp.openById(form_id);
var form_resp = form.getResponses()[0];
var customer_name = form_resp.getItemResponses()[0].getResponse();
Logger.log(customer_name);
// Rest of code follows from here
}
Upon entry for the first form I write:
Name: Peter
Size: M
Comment: none
Code returns:
1:43:40 AM Info Peter
(If I include the rest of the code, it correctly subtracts the inventory).
Then on the next (or tenth) submission, I might submit:
Name: Joe
Size: L
Comment: none
again, the code returns:
1:44:55 AM Info Peter
If I start from scratch or clear responses, I can get it to work once, but if not, the code will forever return "Peter" for the name, regardless of what I enter.
The form responses however are correct! It is just the call to
var form_resp = form.getResponses()[0];
var customer_name = form_resp.getItemResponses()[0].getResponse();
... that seems to have a "cached" value.
This seems like a weird bug, but am wondering if anyone has a clue as to what this might be (or, honestly, if there is a better way to achieve the same result!)
Thanks for taking the time.
Ugh. Very bad error on my part.
As vector pointed out:
The code: form.getResponses() returns all responses whereas I thought it was returning the current response (read the docs!)
Thus form.getResponses()[0] was naturally returning the first answer, whereas I should have written:
var N = form.getResponses().length-1;
var form_resp = form.getResponses()[N];
Whoops.

MySQL query error "Illegal hour value" causing loop and write issues in Google Apps Script

I'm not well-versed in either language, so please bear with me on this. I'm trying to pull a full table with 100 rows from a remote MySQL database into a Google Sheet. I've managed to sort all the issues I've been having with this, but finally feel stuck. It seems the "illegal hour value" error I get from the SQL query during the loop is the main problem.
One of the columns in the MySQL database is "duration", and unfortunately, it can contain durations longer than 23:59:59. I only have access to call the procedure and cannot make any changes to the table. I get the
"illegal hour value"
error when a row hits a duration longer than 24 hours (e.g., 70:00:00). I tried to simplify things by using a try-catch to skip the error and continue writing on the Google Sheet, but then I get the
"number of columns in the data does not match the number of columns in the range.The data has X but the range has Y"
error in the final sheet.getRange() line.
I'm also unable to figure out how to pass multiple statements when executing the MySQL query. I tried to understand addBatch and a couple of other things, but it becomes too complicated for me. Maybe there's a simpler solution with the query, or maybe that's the only solution, because it just might work if I can also add a CONCAT query after the CALL query to convert the "duration" column to string before the data goes into the loop.
The code below has been updated to include the solution:
function getData3(query, sheetName) {
//MySQL (MariaDB) connection and statements.
var user = '';
var userPwd = '';
var url = 'jdbc:mysql://remote.server.example.com/database_name';
var conn = Jdbc.getConnection(url, user, userPwd);
var stmt2 = conn.createStatement();
stmt2.setMaxRows(100);
var rs2 = stmt2.executeQuery('CALL spdAdminGetPIREPS(api_key)');
//Logger.log(rs2)
//Function to convert raw binary data to string to be used for durations >23:59:59.
function byteArrToString(byteArr){
return Utilities.newBlob(byteArr).getDataAsString();
}
//Setting up spreadsheet, results array, and cell range.
var doc = SpreadsheetApp.openById("id");
var sheet = doc.getSheetByName("sheet_name");
var results = [];
var cell = doc.getRange('a1');
var row = 0;
//Loop to get column names.
cols = rs2.getMetaData();
colNames = [];
for (i = 1; i <= cols.getColumnCount(); i++ ) {
//Logger.log(cols.getColumnName(i));
colNames.push(cols.getColumnName(i));
}
results.push(colNames);
//Loop to get row data, catch type errors due to duration >23:59:59 and fix it.
var rowCount = 1;
while(rs2.next()) {
curRow = rs2.getMetaData();
rowData = [];
for (i = 1; i <= curRow.getColumnCount(); i++) {
try {
rowData.push(rs2.getString(i));
} catch (e){
var bytes = rs2.getBytes(i);
rowData.push(byteArrToString(bytes)); //Pushes converted raw binary data as string using function defined above.
//Logger.log(JSON.stringify(rs2.getBytes(i))); //To see the raw binary data returned by getBytes() for durations >23:59:59 that throw an error.
continue;
}
}
results.push(rowData);
rowCount++;
}
//Write data to sheet.
sheet.getRange(1, 1, rowCount, cols.getColumnCount()).clearContent();
sheet.getRange(1, 1, rowCount, cols.getColumnCount()).setValues(results);
//Close result set, conn, and statement.
//Logger.log(results);
rs2.close();
stmt.close();
conn.close();
}
I know the two separate statements and all look ridiculous, but it seems they work, because I don't get the "no database" error with the query anymore. The simpler, single-line JDBC connector did not work for me, hence the current format for connecting to the MySQL server (Mariadb).
If there are no durations in the table longer than 24 hours, the code works and successfully writes the entire table into the Google Sheet.
To sum:
If I don't use try-catch, the loop stops with the error. If I use try-catch and continue, I get the number of columns mismatch error.
The end goal is to call the procedure and write the entire table onto a Google Sheet. Skipping problematic cells I can probably work with, but I'd definitely love to grab all of the data. I might be missing something trivial here, but any direction or help would be appreciated. Thanks in advance!
UPDATE:
I found this question answered here, but cannot figure out how to utilize it in my case. I think that if I can pass multiple queries, I should be able to send a CONCAT query following the CALL query to convert the "duration" column from Datetime (I believe) to string.
UPDATE 2:
#TheMaster's solution for try-catch helped with skipping the problematic cell and continue writing the rest. I'd love to find a way to convert all durations (the entire column or the ones >23:59:59) to string to capture all the data.
UPDATE 3:
Based on #TheMaster's suggestions, using getInt() instead of getString() partially works by returning the hour but not the minutes (e.g., returns 34 if duration is 34:22:00). Need a way to convert when getString() is used.
UPDATE 4 (edited):
Using getBytes(), the values returned are:
[51,52,58,52,56,58,48,48] for 34:48:00
[51,55,58,48,48,58,48,48] for 37:00:00
[56,55,58,48,48,58,48,48] for 87:00:00
[49,53,49,58,51,53,58,48,48] for 151:35:00
Which means:
[48,49,50,51,52,53,54,55,56,57,58] corresponds to [0,1,2,3,4,5,6,7,8,9,:]. How can I incorporate this conversion?
Using getLong(), the values returned are:
34 for 34:48:00, converted to duration -> 816:00
37 for 37:00:00, converted to duration -> 888:00
87 for 87:00:00, converted to duration -> 2088:00
UPDATE FINAL:
#TheMaster's modified answer solved the problem by getting raw binary data and converting to string for durations >23:59:59. Code above has been updated to reflect all modifications; it works as written above.
You currently use MySQL connector, and even if TIME value can be from '-838:59:59.999999' to '838:59:59.999999', MySQL driver throws an exception when getting a value of a TIME type not in the 0-24 hour range.
This can make sense when using Resultset.getTime(i) when not really but doesn't when using Resultset.getString(i).
This can be disabled using noDatetimeStringSync=true, so changing URL to jdbc:mysql://remote.server.example.com?noDatetimeStringSync=true
Disclaimer: I am one of the maintainers of MariaDB java driver, but I would recommend to use MariaDB driver with MariaDB server (and MySQL one with MySQL server). You would have avoid this issue :)
btw, you can directly set database in URL jdbc:mysql://remote.server.example.com/database?noDatetimeStringSync=true avoiding a query.
If you want to skip failed getString(), it should be easy:
try {
rowData.push(rs2.getString(i));
} catch (e){
rowData.push("");//Keeps array straight
continue;
}
If you want to convert the time column to string, you need to use CAST(time AS char) or CONCAT('',time) on the CREATE_PROCEDURE query used to create spdAdminGetPIREPS
Alternatively, You can get the raw binary data using resultSet.getBytes() and change it back to string through blob using Utilities:
function byteArrToString(byteArr){
return Utilities.newBlob(byteArr).getDataAsString();
}
Use it as
var bytes = rs2.getBytes();
rowData.push(byteArrToString(bytes));
If you could directly get blob, it will be easier to get appsScriptBlob
var jdbcBlob = rs2.getBlob();
var blob = jdbcBlob.getAppsScriptBlob();
rowData.push(blob.getDataAsString());
jdbcBlob.free();

FormApp setPoints does not allow "0" as a value

I am writing an App Script using FormApp to reset the values of quiz items to 0. However when I call setPoints(0) it is throwing "Invalid data updating form."
I am trying to set the points of individual items of a form to 0 i.e. basically clear the points of an item. However, when I try to call item.setPoints(0) it is throwing the error of "Invalid data updating form."
Here's my small function that I've written, with dummy form URLs and IDs:
function removePoints() {
var form = FormApp.openByUrl("https://docs.google.com/forms/d/1CXia_B4h7M2Dk1r8mhTLNyoTrz07dwdty5aiTh5v8QA/edit");
var items = form.getItems();
var item = form.getItemById(336898556);
item.asMultipleChoiceItem().setPoints(0);
}
Expected results would be to have set 0 points on given item id.
But instead, I am getting following error:
Invalid data updating form
Is there anything wrong that I am doing? Or is there any other way to reset a item's scores?
Thanks for help in advance. :)

Error Cannot convert Array to (class)[] in making a recurring calendar event with Google Apps Script

I am trying to create a recurring event in a Google Calendar but I keep getting the following error: Cannot convert Array to (class)[]
The problem lies in that I am trying to grab data from a cell to fill in the class. The code is the following:
var recur4 = CalendarApp.newRecurrence().addWeeklyRule().onlyOnWeeks([rep]);
var ne4 = c.createAllDayEventSeries(title, start, recur4, options);
Now, the variable rep is equal to cell H2 which has the following text in it: 31,36
When I put Logger.log(rep); it outputs 31,36 so there is no problem there either.
When I take out rep and put in 31,36 in the brackets, the script works perfectly and adds the events to the calendar, so I know that the problem is not anywhere else in the script.
I suppose that the problem has to do with the formatting in the cell, but I have no idea. Any help would be appreciated.
UPDATE
OK so based on the comment below, I changed the script to the following:
var sp = rep.split(",");
for(var i=0; i<sp.length; i++) { sp[i] = +sp[i]; }
var recur4 = CalendarApp.newRecurrence().addWeeklyRule().onlyOnWeeks(sp);
var ne4 = c.createAllDayEventSeries(title, start, recur4, options);
This got rid of the error, BUT now it is adding events every Friday. In the debugger, it now shows that the array is an integer array and comes out like this: [31,36] which should represent the two weeks I need, but something still does not work and the recur4 remains as undefined instead of an object.
UPDATE
Based on the comments that people gave below, the final script that worked fine was the following:
var recur4 = CalendarApp.newRecurrence().addYearlyRule().onlyOnWeeks(rep.split(",")).onlyOnWeekday(CalendarApp.Weekday.FRIDAY);
var ne4 = c.createEventSeries(title, start, stop, recur4, options);
The issue you have with your EventRecurrence specification is that you are specifying that this event should repeat weekly, but then use a restriction that is incompatible with a weekly restriction.
If you describe your condition with words, note that you cannot avoid saying "year". This is a strong indication that perhaps your recurrence period is incorrect.
E.g. "repeat this event every week, on weeks 31 and 36 of the year" vs. "repeat this event every year, on weeks 31 and 36"
Indeed, changing your restriction from weekly to yearly results in a valid RecurrenceRule:
var recur = CalendarApp.newRecurrence()
.addYearlyRule()
.onlyOnWeeks(rep.split(",").map(
function (week) {
return parseInt(week, 10);
})
);
References:
onlyOnWeeks
addYearlyRule
PS: the EventRecurrence and RecurrenceRule classes are pretty much interchangeable.

Custom Function re-running itself periodically

I've created a google spreadsheet that uses a custom function to call a paid API service and parse that results based on a few input parameters. I am able to call and parse the data successfully using an activation button (A1 is a cell that if its value is "ON" the script is called).
My issue is that if I leave the data parsed by leaving the activation button "ON", the custom function re-runs itself. If I were to change an input parameter I would understand it re-running itself, but I can sit there staring at the screen, and it will once again show "Loading..." and parse the data again, as if I had set the button to "OFF" and "ON" again. This seems to happen at random - it can be 5 minutes or 2 hours.
I'm attaching the function below, as well as the logic of the spreadsheet
/// This function injects the variables into the HTTP service and if the data can be parsed returns an array.
function getCategory (category,key){
var apiurl = "https://SERVICE/"+category+"/Country?Format=JSON&id="+key
var result = []
try {
var category_data = parse(apiurl)
var data_dictionary = category_data.TopCountryShares
for (var i in data_dictionary){
result.push(data_dictionary[i].CountryCode)
}
}
catch(e) {
result.push("No Data")
}
return result
}
//// This is the function that parses the data above
function parse(url){
var parsing_url = url
var fetchapi = UrlFetchApp.fetch(parsing_url)
Utilities.sleep(2000)
var data=JSON.parse(fetchapi)
return data
}
In the Spreadsheet I would have A1 as the ON/OFF button, and A5 down as individual categories (i.e A5 = Games, A6 = Shopping, etc). The below excel formulas would be used:
B5 would be =IF(AND($A$1="ON",ISBLANK(A5)=FALSE),TRANSPOSE(getCategory(A5,'API KEY'!$B$6)),"")
B6 would be =IF(AND($A$1="ON",ISBLANK(A6)=FALSE),TRANSPOSE(getCategory(A6,'API KEY'!$B$6)),"")
In case I didn't explain it correctly, a scenario would be as follows:
Button is set to OFF
I enter 20 categories into A5:A25
I turn Button ON
Loading...
Data is parsed for each category from B-F
I don't touch anything else in the document and on occasion I see "Loading..." again as the script is re-running
Number 6 seems to happen at random time intervals, and I can confirm that the "Recalculation" is set to "On Change" (found in File -> Spreadsheet Settings -> Calculation)
The above is a sample script, but the real script returns JSONs that can be quite large, so caching isn't an option based on what I've read of the current limitations.
Any help would be appreciated - I'm at my wits end trying to figure this out!
I assume if you set the button to off to doesn't refresh? Would it be an option to add a line of code to the end of your script to set the button to off after the script has finished?
sheet.getRange('A1').setValue('OFF');