I have set up my google analytics API to generate a report within google sheets and feed it data from the given analytics view (profile) ID. I know that you can only run a report from one ID, so I would like to use JS to iterate through all 35 of them. I know enough JS to loop through a grouping, but how would I call the ID's? I will be using the google app script since it is already integrated. any ideas? thanks in advance!
*EDIT
so here is a little more detail. i have a google analytics account that monitors about 35 sites (all unique profile ID's that i have listed and have permissions to). those stats have been manually added to a google sheets doc up until this point. i want to grab the data from each of those Profile ID's and populate an existing spreadsheet without having to run 35 individual reports, which will create 35 individual sheets. i am already able to pull the reports one by one; writing a script to iterate through all the profiles and populating an existing spreadsheet (all my efforts thus far have just created new tabs on the existing sheet) is what i am wrestling with. thank you for your patience and time! happy holidays!
Here are copy/pasted from a Google Spreadsheet I use a few functions, one that lists all accounts for which the user is authenticated, one that returns all properties in an account (pass in account id as parameter) and one that lists all views in a property of a given account (pass in property and account id as parameter). The functions are written to return JSON objects where each element containts key/value pairs for name and (internal) id. Simply use the output of previous functions as parameters for the subsequent functions.
Note that for historical reasons the function that return the view info is named Analytics.Management.Profiles.list, "profiles" being what views used to be called before Universal Analytics.
/* Generates a list of accounts that are accessible to the currently logged in user */
function listAccounts() {
var accounts = Analytics.Management.Accounts.list();
var items = accounts.getItems();
var item;
var accountList = [];
if (items) {
for (i = 0, cnt = items.length; i < cnt; i++) {
item = items[i];
accountList.push({
"name": item.name,
"id": item.id
});
}
}
return accountList;
}
/* .... and all the properties in those accounts .. */
function listProperties(accountId) {
var webProperties = Analytics.Management.Webproperties.list(accountId);
var items = webProperties.getItems();
var item;
var propertyList = [];
if (items) {
for (i = 0, cnt = items.length; i < cnt; i++) {
item = items[i];
propertyList.push({
"name": item.name,
"id": item.id
});
}
}
return propertyList;
}
/* And all data view in the selected property */
function listViews(accountId, propertyId) {
var views = Analytics.Management.Profiles.list(accountId, propertyId);
var items = views.getItems();
var item;
var viewList = [];
if (items) {
for (i = 0, cnt = items.length; i < cnt; i++) {
item = items[i];
viewList.push({
"name": item.name,
"id": item.id
});
}
}
return viewList;
}
Related
I am trying to create a scheduled suite script where it gets a sublist and applies some conditions to evaluate the result and send automated mail. But I couldn't get the sublist ID and can't retrieve the sublist data. Can I know where to get the sublist ID and how to create a way to retrieve the sublist data?
var sublistlines = currentRecord.getLineCount({
sublistId: 'SublistID',
});
for (var i = 0; i < sublistlines; i++) {
var item = currentRecord.getSublistValue({
sublistId: 'SublistID',
fieldId: 'SublistFieldId',
line: i
});
if(condition){
action()
}
Sublistlines returns -1
You can get the record's details of its sublist ids and field ids using schema browser here is the URL and find the record with starting letter, in lower case there will be internal ids of each fields or sublist. Schema browser URL-
https://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2016_1/script/record/salesorder.html
And you can also download field explorer chrome extension for netsuite through google and use it to view field ids on records.
Field Explorer chrome extension-
https://chrome.google.com/webstore/detail/netsuite-field-explorer/cekalaapeajnlhphgdpmngmollojdfnd?hl=en
You can use the getSublists() method to get all sublists deployed to a record. Here is an example script that I wrote that if called should give you all the values associated to all sublists related to an invoice record.
// get an array of all sublistId's available to the invoice record.
// you can use this with any record type and can perform this in client script or the debugger to find the sublist you're looking for.
var rec = record.load({type: record.Type.INVOICE, id: invoiceId});
var sublistIdArr = rec.getSublists();
// if you want to, you get more information about each sublist
for (var i = 0; i < sublistIdArr.length; i++) {
var sublistId = sublistIdArr[i];
var sublistFields = rec.getSublistFields({sublistId: sublistId});
var sublistLineCount = rec.getLineCount({sublistId: sublistId});
for (var line = 0; line < sublistLineCount; line++) {
var sublistObject = {};
for (var j = 0; j < sublistFields.length; j++) {
var fieldId = sublistFields[j];
sublistObject[fieldId] = rec.getSublistValue({sublistId: sublistId, fieldId: fieldId, line: line});
}
// do whatever you want with that sublist object here to see what is information is available.
}
}
So, i'm building a integration with NetSuite, and i need a SuiteScript that gets all SalesOrders based on a status filter. I was able to create scripts that retrieve based on creation date and SO Number, but for some reason, the one using the Status field does not work, seems like i'm using the wrong field for the filter or something, could you please provide me some examples on how you've done it?
Below is my script for reference:
function SearchOrdersByStatus(status){
var allSalesOrders = [];
var salesOrdersIds = [];
var start = 0;
var pageSize = 1000;
var filters = [new nlobjSearchFilter('status', null, 'is', status)];
var columns = [new nlobjSearchColumn('internalid')];
var searchResults = nlapiSearchRecord('salesorder', null, filters, columns);
do {
if (searchResults.length > 0) {
for (var i = 0; i < searchResults.length; i++) {
salesOrdersIds.push(searchResults[i].getValue('internalid'));
var salesOrder = nlapiLoadRecord('salesorder', salesOrdersIds[i]);
allSalesOrders.push(salesOrder);
}
start += pageSize;
}
} while (searchResults.length >= pageSize);
return allSalesOrders;
}
transactions search status is easiest to get by creating and saving a saved search that filters on the statuses of interest and then listing those statuses by extracting them from the saved searches in the console.
see the second script here: NetSuite Search formula for items that have no open transactions
the status values you'll use in the search will look like:'SalesOrd:A', 'SalesOrd:B' etc
I'm creating a GAS for Gmail.
The first part of this script is collecting all recipients and senders from a thread object from the GmailApp. No problem there, except I'm just left with an array of strings with the "full name <email>" syntax.
Secondly, I want to create a card for each contact email, by first looking up the contact object inside the ContactsApp. This however causes the script to time out, even though logs show it is able to fetch the contacts.
I've tested 2 alternatives:
get all contacts with ContactsApp.getContacts() and there's no timeout.
look up a single contact with CotnactsApp.getContact(email)an no timeout either.
// TEST 1: Get All Contacts
var allContacts = ContactsApp.getContacts();
// TEST 2: Get one contact
var testContact = ContactsApp.getContact("test#email.com");
// TEST 3: Loop over and get contacts
var threadContacts = [];
for (var i = 0; i < participants.length; i++) {
var email = participants[i].split("<")[1].split(">")[0];
var contact = ContactsApp.getContact(email);
threadContacts.push(contact)
}
So I'm a bit unclear here as to the intended usage. Should I go for option 1 and load all the contacts on the client side and iterate through these? Or is it intended to look for a contact each time I need to get the data? Doesn't loading all contact risks loading a gazillion contacts (should someone have so many?). It seems cumbersome.
I couldn't see any guidelines on the documentation.
You want to retrieve the contact object by searching the emails.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
In this answer, the contact objects are retrieved by searching the emails from ContactsApp.getContacts(). This method is also mentioned in your question.
Flow:
The flow of this sample script is as follows.
Create an array by retrieving email from "full name <email>".
Retrieve all contacts.
Retrieve the contact object by searching the emails from all contacts.
Sample script:
In this case, it supposes that participants is an array like ["full name <email1>", "full name <email2>",,,].
// Retrieve email address from "full name <tanaike#hotmail.com>" in participants and put them to an array.
var convertedParticipants = participants.map(function(e) {return e.split("<")[1].split(">")[0]});
var allContacts = ContactsApp.getContacts();
// Here, the contact objects are retrieved by searching the emails.
var results = allContacts.reduce(function(ar, c) {
var emails = c.getEmails();
if (emails.length > 0) {
// Retrieve email addresses from a contact object and put them to an array.
var addresses = emails.map(function(e) {return e.getAddress()});
// When the emails of "convertedParticipants" are included in the contact object, the object is put to an array.
if (convertedParticipants.some(function(f) {return addresses.indexOf(f) != -1})) {
ar.push(c);
}
}
return ar;
}, []);
When above script is run, you can see the searched contact objects in results as an array.
References:
getEmails()
getAddress()
reduce()
some()
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
When var results = allContacts.reduce(function(ar, c) {###}, []); is modified using the for loop, it becomes as follows.
Script:
var results = [];
for (var i = 0; i < allContacts.length; i++) {
var emails = allContacts[i].getEmails();
if (emails.length > 0) {
var addresses = [];
for (var j = 0; j < emails.length; j++) {
addresses.push(emails[j].getAddress());
}
var f = false;
for (var j = 0; j < convertedParticipants.length; j++) {
if (addresses.indexOf(convertedParticipants[j]) != -1) {
f = true;
break;
}
}
if (f) {
results.push(allContacts[i]);
}
}
}
I have a database with two tables: 'GroupTable' & 'ItemTable'. GroupTable stores the group information such as group name etc. The ItemTable stores the item information as well as which group they belong to. Here's the thing, i am not using foreign key for some reason which i will not explain.
GroupTable: id, name, code
ItemTable: id, name, code, group
The ItemTable also stores the group, differentiated by group='-1'. So what i did was: Find all items in ItemTable with 'group = -1', get the codes of all the items, then filter the GroupTable and find all groups that has codes from the result. Because i need the group names of those groups which are in the ItemTable.
var grp = -1;
$.post("ItemTable/getItems", {group:grp}, function(result){
if(!result.error){
var count = result.data.length;
var data = result.data
var codes = new Array;
for (var i=0; i<count; i++) {
groups.push(data[i].code);
}
$.post("GroupTable/getItem", {code:codes}, function(result2) {
if (!result2.error){
var grpCount = result2.data.length;
var grpData = result2.data;
for (var i=0; i<grpCount; i++){
var name = grpData[i].name;
//DO SOMETHING WITH THE NAME HERE
}
}
}
}
}
Is there anyway i can achieve the same result using only a single post? Or am i looking at this wrongly and i should be editing my getItems function in my controller instead? Currently i am using 'TableItem.find(queryItem).exec()', how should i be writing my code to fit what i am trying to achiveve?
And yes let's assume for some reason i am not able to use foreign key, and that my code will ensure the 'code' is unique in GroupTable.
EDIT:
I am unable to copy and paste as i am working in an offline environment. I am using a MVC model, the code above are the codes for my view. As for the function 'getItems' in my controller, this is the code in my controller(summarized as i have to type this out)
getItems: function(req, res){
var id, name;
(typeof(req.params.id) === 'undefined') ? id=req.body.id : id=req.params.id;
(typeof(req.params.name) === 'undefined') ? name=req.body.name : name=req.params.name;
var queryItem = {
id: id,
name: name
};
// there is more than just id and name, as i did not type everything out
for (var propertyName in queryItem) {
if (queryItem[propertyName] == null){
delete queryItem[propertyName];
}
}
ItemTable.find(queryItem).exec(function(err, items){
if (err)
red.json({"error":err, data:null});
else
res.json({"error":null, data: items});
});
I have built a script that pulls from multiple spreadsheets and calculates total values. Basically what happens is multiple employees will input hours performed on a particular task for a particular customer. I then want to calculate how much work was done for a particular customer on a master spreadsheet and pull some analytics.
So on the master spreadsheet, there is a tab for each customer. This function takes the sheet names from the master spreadsheet and creates an array of all of the customer names. The reason why I do this is so that if a new tab is created, that customer name will automatically be included in the customer array.
function getCurrentCustomers() {
var sheets = servicesSpreadsheet.getSheets();
for (var i=0 ; i < sheets.length ; i++) {
if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files") {
currentCustomers.push(sheets[i].getName());
};
};
};
The next function takes a look at all of the files in a particular Google Drive folder and returns the IDs in an array. This allows me to create a copy of an employee spreadsheet stored in this particular folder, and that spreadsheet's values will automatically be calculated as they change.
function listFilesInFolder() {
var folder = DriveApp.getFolderById("0B7zrWIHovJrKVXlsaGx0d2NFT2c");
var contents = folder.getFiles();
var cnt = 0;
var file;
while (contents.hasNext()) {
//Finds the file in the specified folder
var file = contents.next();
//Increases the count
cnt++;
//Gets the Id of the file
data = [
file.getName(),
file.getId()
];
//Appends it to the employeeId list
employeeIds.push(data);
};
return employeeIds;
};
The last function is the one that is slowing it down a great deal.
First I create an array for all of the possible services. Unfortunately there are 137 individual services.
Then I loop through all of the customers. For each customer, I loop through every service to see if it appears on any of the employees spreadsheets. I am thinking there is a more efficient way to do this. The Google Script times out before I even get through one full customer. Also, I haven't even included the spreadsheets for all of the employees yet. I am just testing using dummy data.
function calculateNumbers(){
var allServices = servicesSpreadsheet.getSheetByName("Services").getRange("Z2:Z137").getValues();
Logger.log(allServices);
Logger.log(allServices[0][0]);
employeeList = listFilesInFolder();
//Gets services spreadsheet range
/*Loops through all of the current customers (currentCustomers comes from function getCurrentCustomers)*/
for (var c = 0; c < currentCustomers.length; c++) {
var currentCustomer = currentCustomers[c];
var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn();
var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues();
//Loops through all of the services
var serviceTotal = 0;
for (var service = 0; service < allServices.length; service++){
//Loops through employee spreadsheet Ids
for (var i = 0; i < employeeList.length; i++) {
//Get employee spreadsheet ID
var spreadsheetId = employeeList[i][1];
//Open the employee spreadsheet by ID
var employeeSpreadsheet = SpreadsheetApp.openById(spreadsheetId);
//Get the sheets from the particular employee spreadsheet
var sheets = employeeSpreadsheet.getSheets();
//Gets the name of each sheet in the employee spreadsheet
var sheetsName = [];
for (var j = 0; j < sheets.length; j++) {
sheetsName.push(sheets[j].getName());
};
//Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet
for (var q = 0; q < sheetsName.length; q++) {
if (sheetsName[q] != "Services") {
//Gets the range of the spreadsheet
var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues();
//Loops through the range to see if range matches service and customer
for (var r = 0; r < range.length; r++) {
if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) {
serviceTotal += range[r][4];
};
};
};
};
};
//Adds the service total to the correct customer's spreadsheet
for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){
var end = 0;
if (end > 0) {break}
else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {
servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal);
end += 1;
};
};
};
};
};
The first image shows what an employee spreadsheet looks like. The second shows what an individual customer sheet looks like. There are many customer sheets on the master spreadsheet.
One of the things you will notice, is that each service has a category. I was thinking maybe to check the category and then check the particular service. There are 23 categories.
I was also hoping there was a way to only look at services that have actually had work done on them. If no one has ever done a Campaign Setup, maybe that could be ignored.
Any help would be greatly appreciated. I apologize for the lengthy post!
You are calling services inside of loops multiple times. These can take up to a couple seconds each, and are generally considered to be very slow. This is against Apps Script best practice as it greatly increases your execution time.
TL;DR: You are calling Apps Script services potentially thousands of times. Each call can take up to a couple seconds to execute. You need to cache your service calls outside of your loops, otherwise your performance is going to suffer horribly.
Examples:
1:
if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files")
Create and set a variable with the sheet name once, and check that instead of calling the getName() method twice. This isn't a huge deal, but will increase execution time.
2:
This is a biggie, as it's one level deep in your loop in calculateNumbers
var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn();
var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues();
2a:
You are opening a new spreadsheet, opening a new worksheet, and then getting a range for the same sheet, and getting the values of that range once per loop for your servicesRange. These service calls will stack up quick, and bloat your execution time.
2b:
I see you are getting the lastColumn, but I don't see it used anywhere? Maybe I missed something, but it going unused while making a service call for each loop will add even more to your execution time.
3:
This is massive, you are potentially calling Apps Script services thousands or tens of thousands of times here. This snippet is already two loop levels deep.
//Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet
for (var q = 0; q < sheetsName.length; q++) {
if (sheetsName[q] != "Services") {
//Gets the range of the spreadsheet
var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues();
//Loops through the range to see if range matches service and customer
for (var r = 0; r < range.length; r++) {
if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) {
serviceTotal += range[r][4];
};
};
};
};
};
//Adds the service total to the correct customer's spreadsheet
for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){
var end = 0;
if (end > 0) {break}
else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {
servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal);
end += 1;
};
};
};
You have multi-level nested loops in nested loops calling the same services. If you had a thrice nested loop with each level being iterated 10 times, and the bottom level calling a service once per loop. You would be calling an Apps Script service 1,000 times. Being conservative, at let's say 0.5 seconds per service call, you would already have 8.3 minutes of execution time.
Cache your service calls, and perform bulk operations.