How to make google sheet script Ignore fields with formulas - javascript

I have built a script which works perfectly fine, however for usability I want to populate the fields queried to build the payload, via formulas.
Issue: if I have fields without values but formulas, the script sends empty values to the api resulting in an error stopping follow up functions.
If I manually set the array of fields including formulas it works.
Solution I am looking for: a code line for my script, to break upon meeting a field with a formula instead of a value.
I hope this question/issue is clear.
I tried this code but it did not work:
for (var i = 1; i < data_statistics.length; i++) {
if (data_statistics[i] === undefined) {
break;
}
else if (data_statistics[i][0].includes("=")) {
break;
}
This is the working function:
function Mean(){
var sheet_statistics = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Mean")
var data_statistics = sheet_statistics.getDataRange().getValues();
var token = _requestToken();
var col_start = 2
for (var i = 1; i < data_statistics.length; i++) {
if (data_statistics[i] === undefined) {
break;
}
// Build payloads and apply small sanity checks payload specific
// Payload same typo
var payload = Object();
for (var j = col_start; j < data_statistics[0].length; j++) {
_buildPayload(payload, data_statistics[0][j].split('.'), data_statistics[i][j]);
}
payload.filters.divisionLevel100 = String(payload.filters.divisionLevel100);
// Get statistics
var statistics = _getOfferStatistics(token, payload)
// Set statistics values in cells
sheet_statistics.getRange(i+1, 1).setValue(statistics.metric)
sheet_statistics.getRange(i+1, 2).setValue(statistics.value)

Use getFormulas(), which returns
empty strings for no formulas
const data_statistics_rg = sheet_statistics.getDataRange(),
data_statistics = data_statistics_rg.getValues(),
data_statistics_formulas = data_statistics_rg.getFormulas();
for (var i = 1; i < data_statistics.length; i++) {
if (data_statistics[i] === undefined) {
break;
}
else if (!data_statistics_formulas[i][0].includes("")) {
break;
}

Related

HTML Logic Form Obfuscating Password

I'm attempting to troubleshoot a login issue on an old system that noone here is very familiar with. We have what we believe to be the admin password, but it isn't working. I'm just grasping, but I thought maybe a browser issue, considering how old the system is, so I tried using Postman to see what kind of response I get, which resulted in a failure.
However, I'm noticing now that they seem to be using some method to obfuscate the password, and I don't really understand what it's doing.
The login form method is this.
<form method="post" name='login' action="/?language=en" onsubmit="document.getElementById('submit').disabled = true; document.getElementById('pass').value = CJMaker.makeString(document.getElementById('pass').value);" >
and the CJMaker file contains this.
function CJMaker(e)
{}function _CJMaker_makeString(val)
{if (val == null)
{return val;}var size = val.length;var retVal = new String();var conVal = new String();for (var i = 0; i < size; i++)
{var current = val.charCodeAt(i);current = current^4;conVal += String.fromCharCode(current);}for (var i = size - 1; i >= 0; i--)
{retVal += conVal.charAt(i);}return retVal;}CJMaker.makeString = _CJMaker_makeString;
So it looks like it's just using char codes, and I suspect that the password in the database isn't the actual password, but instead is whatever this would create.
I'm afraid I just do not understand this well enough to reverse it. I'm sure it's simple to some of you javascript guys though.
Can anyone tell me more about what this is doing?
The function XORs the character code of each character with 4, and then reverses the result.
This function is its own inverse, so if you have an encoded password on the server, run the function on that to get what you need to type.
function CJMaker(e) {}
function _CJMaker_makeString(val) {
if (val == null) {
return val;
}
var size = val.length;
var retVal = new String();
var conVal = new String();
for (var i = 0; i < size; i++) {
var current = val.charCodeAt(i);
current = current ^ 4;
conVal += String.fromCharCode(current);
}
for (var i = size - 1; i >= 0; i--) {
retVal += conVal.charAt(i);
}
return retVal;
}
CJMaker.makeString = _CJMaker_makeString;
let instring = "abcdefgh";
let obfusc = CJMaker.makeString(instring);
let outstring = CJMaker.makeString(obfusc);
console.log(instring, obfusc, outstring);

Setting tableau filters via JS across multiple dashboards/worksheets

We have a workbook which contains two dashboards, the first contains one worksheet and the second contains four worksheets.
We're trying to pass filters in via the url (that part is ok) but we cannot get all the worksheets on the second dashboard to update.
The code loops through the dashboards, activating each in turn then calling a filterActiveSheet() method on each.
This method loops through each worksheet in turn, searches for a matching categorical filter with the same FieldName as the provided one and, when found, uses the applyFilterAsync() method to replace it with the provided one.
var options = {
< snip >
onFirstInteractive: function () {
workbook = viz.getWorkbook();
sheets = workbook.getPublishedSheetsInfo();
for(s = 0; s < sheets.length; s++)
{
viz.getWorkbook().activateSheetAsync(s)
.then(filterActiveSheet);
}
}
};
function filterActiveSheet(sheet) {
for (ws = 0; ws < sheet.getWorksheets().length; ws++) {
var worksheet = sheet.getWorksheets()[ws];
worksheet.getFiltersAsync()
.then(function(p) {
var f = filters.split(';');
for(y=0;y<f.length;y++){
var filter = f[y].split(':');
var filterType = $.grep(p, function(e){ return e.getFieldName() == filter[0]; });
if (filterType.length > 0) {
switch(filterType[0].getFilterType()) {
case tableau.FilterType.CATEGORICAL:
return worksheet.applyFilterAsync(filter[0], filter[1], tableau.FilterUpdateType.REPLACE);
break;
< snip >
}
}
}
});
}
}
var viz = new tableauSoftware.Viz(placeholderDiv, url, options);
The problem we're seeing is that only one worksheet on each dashboard is being updated. Stepping through the JS in Chrome I can see the call to applyFilterAsync() the expected number of times which does not seem to cause an error, it simply doesn't seem to want to update all the filters/worksheets, just one on each dashboard.
Thoughts? Suggestions? Syntax errors?
getPublishedSheetsInfo() returns a collection that is returning one worksheet and one dashboard the types of which can be determined by calling getSheetType(). You can't apply getFilterAsync() directly to a dashboard but have to iterate. Try something like:
if (sheet.getSheetType() === 'WORKSHEET') {
sheet.getFiltersAsync().then(function(filters) {
for (var x = 0; x < filters.length; x++) {
// do something
}
})
} else {
// either in a dashboard or story
var workSheetArray = sheet.getWorksheets();
for (var i = 0; i < workSheetArray.length; i++) {
workSheetArray[i].getFiltersAsync().then(function(filters) {
for (var x = 0; x < filters.length; x++) {
// do something
}
}
}
}

Extracting numbers from Gmail messages using Google Apps Script

I'm trying to extract numbers such as the ones listed below from my Gmail messages using Google Apps Script.
2,495.00
1,594
3,777.23
642.00
This is the code:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search('subject:(Transaction) after:2016/7/31 before:2016/8/10');
for (var i=0; i<threads.length; i++)
{
var messages = threads[i].getMessages();
var tmp;
for (var j=0; j<messages.length; j++)
{
var content = messages[j].getBody();
var subject = messages[j].getSubject();
var date = messages[j].getDate();
Logger.log(content);
if (content)
{
tmp = content.match(/\d+(,\d+)*(\.\d+(e\d+)?)?/);
var number = (tmp && tmp[j]) ? tmp[j] : 'No number';
sheet.appendRow([number, subject, date]);
}
else
{
sheet.appendRow([content, subject, date]);
}
}
}
}
I've been getting mixed results. For some messages this works as intended but for some it completely skips the numbers from the messages. I'm a newbie to JS/GAS and I thought the problem was in the regex but I'm not sure. Any help in this would be appreciated.
you are facing two trouble here:
the regex you are using don't look optimised (but neither what you are looking is clear a number like 1,594 should not be found if you are also looking at number that look like that 642.00). Nevertheless you could use a a regex like Shekhar Khairnar proposed in comment or something similar (the gat the end is important as there is more than one number in your mail).
The second trouble is in the line var number = (tmp && tmp[j]) ? tmp[j] : 'No number';. Why is there a jvar in this line? jis reference to the for loop --> number of messages, nothing to do with the occurences in your message.
What I can propose you is something like that:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search('test');
var re = /(?:\d{1,3}[,])*\d{1,3}\.{0,1}\d{1,3}/g;
for (var i=0; i<threads.length && i<5; i++) // added a condition because I didn't wanted to have too many results
{
var messages = threads[i].getMessages();
var tmp;
for (var j=0; j<messages.length; j++)
{
var content = messages[j].getPlainBody(); //.getBody();
var subject = messages[j].getSubject();
var date = messages[j].getDate();
//Logger.log(content);
if (content)
{
tmp = content.match(re); // /\d+(,\d+)*(\.\d+(e\d+)?)?/);
var number = tmp || ['No number']; // result of tmp is either null or an array --> if it's null then it will take the value 'no number'
Logger.log(number);
sheet.appendRow([number.join(" | "), subject, date]);
}
else
{
sheet.appendRow([content, subject, date]);
}
}
}
}

Unpredictable behaviour when setting a boolean variable in JavaScript [Deep copying in strict mode]

function computerSetManapool(cost) {
var splittedCost = cost.split(',');
for (var i = 0; i < splittedCost.length; i++) {
if (splittedCost[i] === "CL") {
for (var j = 0; j < computerLands.length; j++) {
if (!computerLands[j].tapped) {
computerLands[j].tapped = true;
console.log(computerLands[j].name + " gets tapped");
break;
}
}
}
}
}
I'm having a bit of trouble with this little piece of code, it's a Windows Store App, though I'm not using anything like the WinJS library or jQuery or what not.
Especially this line worries me:
computerLands[j].tapped = true;
I was debugging this in Visual Studio, while j had the value of 1. Therefore computerLands[1].tapped was supposed to be set to true.
Instead it set computerLands[1].tapped = true AND computerLands[3].tapped = true.
It does not happen every time, but many times and therefore I can't see what the problem is.
computerLands is an initially empty array, which then gets dynamically pushed objects into it.
If someone even has a remote idea what problem this could be, I would be really grateful.
Edit: This is the code where computerLands gets populated:
function computerTurn() {
untapAll("computer");
drawCards(1, "computer");
for (var i = 0; i < computerHand.length; i++) {
if (computerHand[i].type === "land") {
var container = document.getElementById("computerLandsContainer");
var newItem = document.createElement("div");
newItem.id = computerLandsID;
computerLandsID++;
newItem.style.backgroundImage = "url("+computerHand[i].image+")";
newItem.style.backgroundSize = "100%";
var btn1 = document.createElement("button");
btn1.id = "tapButtonComputer";
btn1.innerText = "˜";
newItem.appendChild(createEnlargeButton(newItem,"computer"));
newItem.appendChild(btn1);
container.appendChild(newItem);
computerLands.push(computerHand[i]);
console.log("Computer plays: " + computerHand[i].name);
computerLands[computerLands.length - 1].id = newItem.id;
var index = computerHand.indexOf(computerHand[i]);
computerHand.splice(index,1);
break;
}
}
}
The actual line is just this:
computerLands.push(computerHand[i]);
After pushing it into computerLands it gets removed from computerHand via splice().
var index = computerHand.indexOf(computerHand[i]);
computerHand.splice(index,1);
Finally I found a solution, I got it from this page: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/f79b2e68-15a9-4d5e-8aad-e15f78d94840/how-do-i-clone-an-object?forum=winappswithhtml5
It describes how to deep copy in Javascript in Windows Store Apps (this other solution I found http://james.padolsey.com/javascript/deep-copying-of-objects-and-arrays/ is not allowed in Windows Store Apps strict mode), basically you create a new object and copy every property into it. Now here's my new working code:
function populateComputerLands() {
for (var i = 0; i < 4; i++) {
//computerLands.push(availableComputerCards[0]); <--- old code
var objectToCopy = availableComputerCards[0]; // new code
var newObject = Object.create(Object.getPrototypeOf(objectToCopy));
var newObjectProperties = Object.getOwnPropertyNames(objectToCopy);
var propertyName;
for (var p in newObjectProperties) {
propertyName = newObjectProperties[p];
Object.defineProperty(newObject, propertyName, Object.getOwnPropertyDescriptor(objectToCopy, propertyName));
};
computerLands.push(newObject);
}
}

Pull data from one Google spreadsheet to another using Google script

I have 2 spreadsheet in my Drive. I want to pull data from a cell in 1 spreadsheet and copy it in another.
The spreadsheet "TestUsage" will sometimes have data in column A, but none is column B.
I would like so that when I open the spreadsheet, it would populate that empty cell in sheet "TestUsage" from sheet "TestParts".
Here is my code:
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 4);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 3);
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
Logger.log(rangeTestUsage[i][1]);
Logger.log(rangeTestParts[j][1]);
rangeTestUsage[i][1] = rangeTestParts[j][1];
break;
}
}
}
}
}
The problem with this is this doesn't do anything:
rangeTestUsage[i][1] = rangeTestParts[j][1];
I know there must be a method that can get data from one range to another.
Please let me know if I am totally incorrect or I am on the right path.
the statement
"this doesn't do anything:"
rangeTestUsage[i][1] = rangeTestParts[j][2];
is not really true... and not really false neither..., actually it does assign the value to rangeTestUsagei but you dont see it because it is not reflected in the spreadsheet.
Both values are taken from the Sheet using getValues so at that time they are both array elements.
What is missing is just writing back the array to the sheet using the symetrical statement setValues()
Give it a try and don't hesitate to come back if something goes wrong.
EDIT :
I didn't notice at first that you were using getSheetValues instead of getValues (simply because I never use this one)... the only difference is that getValues is a method of the range class while yours belongs to the sheet class; the syntax is similar in a way, just use
Sheet.getRange(row,col,width,height).getValues()
it takes one word more but has the advantage to have a direct equivalent to set values
Sheet.getRange(row,col,width,height).setValues()
Serge insas has a good explanation of why your code doesn't work and hints at the solution below.
I recommend you use an array to store the updated values of column B that you want then set the entire column at the end.
Modifying your code...
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 2);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 2);
var colB = [];
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
var matched = false;
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
//Logger.log(rangeTestUsage[i][1]);
//Logger.log(rangeTestParts[j][1]);
colB.push([rangeTestParts[j][1]]); // push the value we want into colB array
matched = true;
break;
}
}
if(!matched) // this is in case you don't have a match
colB.push([""]); // incase we don't have a matching part
} else {
colB.push([rangeTestUsage[i][0]]); // we already have a value we want so just add that to colB array
}
}
sTestUsage.getRange(2,2,lastRowTestUsage).setValues(colB); // update entire column b with values in colB array
}

Categories