I have this piece of code:
function GetData(evt){
var adresses = new Array();
var j = 0;
var excel = new ActiveXObject("Excel.Application");
var fil = document.getElementById("file");
var excel_file = excel.Workbooks.Open(fil.value);
var excel_sheet = excel.Worksheets(1);
for(var i=2;i<500;i++){
var morada = excel_sheet.Range("E"+ i );
var localidade = excel_sheet.Range("C"+ i );
var pais = excel_sheet.Range("A"+i);
adresses[j] = (morada + ", " + localidade + ", " + pais);
j++;
}
for(var k = 0; k<j; k++) {
codeAddress(adresses[k]);
}
}
It receives an excel file and processes the data like I want. The thing is, it is very hard coded.
For instant, in this for:
for(var i=2;i<500;i++)
I am using 500, but I would like to use the number of rows in the sheet. I have already tried a few things like rows.count and whatever and I gave some alerts to see the results, but I just can't find the one who tells me the number of rows.
Anyone know how to do it?
Try that :
excel_sheet.UsedRange.Rows.Count
Does it works ?
Related
I have a GS code with three different functions that are activated with a form submission.
The first function:
function processForm(formObject){
if(formObject.RecId && checkID(formObject.RecId)){
updateData(getFormValues(formObject),globalVariables().spreadsheetId,getRangeByID(formObject.RecId));
}else{
appendData(getFormValues(formObject),globalVariables().spreadsheetId,globalVariables().insertRange);
var form = FormApp.create(formObject.nombre)
.setAllowResponseEdits(false)
.setCollectEmail(false)
.setDescription(formObject.fechaprev)
.setRequireLogin(false)
.setShowLinkToRespondAgain(false);
var newname = formObject.nombre;
form.setDestination(FormApp.DestinationType.SPREADSHEET, globalVariables().spreadsheetId);
}
When this first function completes I need to run this other two functions:
function ordenarData(formUrl){
var ss = SpreadsheetApp.openById(globalVariables().spreadsheetId);
var sheetNameArray = [];
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
sheetNameArray.push(sheets[i].getName());
}
var ordenado = sheetNameArray.sort();
for( var j = 0; j < sheets.length; j++ ) {
ss.setActiveSheet(ss.getSheetByName(sheetNameArray[j]));
ss.moveActiveSheet(j + 1);
var last_element = sheetNameArray[sheetNameArray.length - 1];
Logger.log(last_element);
}
completarData(formUrl,last_element);
}
function completarData(formUrl,last_element){
var ss = SpreadsheetApp.openById(globalVariables().spreadsheetId);
var hojaData = ss.getSheetByName("Data")
var celdaNEv = "=COUNT(\'"+ last_element + "\'\!\A\:\A\)";
var celdaSatisf = "=IFERROR((AVERAGE(\'"+ last_element +"\'\!\C\:\C)/10);0)";
var celdaPEv = '=IFERROR(O'+ hojaData.getLastRow() +'/N' + hojaData.getLastRow() +';0)';
var celdaPAs = '=IFERROR(N'+hojaData.getLastRow()+'/M'+hojaData.getLastRow()+';0)';
var celdaForm = "<a href=" + formUrl + " target='_blank' class='btn btn-primary btn-xs'><i class='fas fa-file-alt'></i></a>";
var celdaTh = '=IFERROR(ROUND(L'+hojaData.getLastRow()+'*N'+hojaData.getLastRow()+'; 0);0)';
var celdaMhr = '=IFERROR(ROUND(T'+hojaData.getLastRow()+'/M'+hojaData.getLastRow()+'; 2);0)';
var formulas = [
[celdaNEv, celdaSatisf, celdaPEv, celdaPAs, celdaForm, celdaTh, celdaMhr]
];
var formatos = [
["###", "0.00%", "0.00%", "0.00%", "", "####", "0.00"]
];
var cell = hojaData.getRange("O"+hojaData.getLastRow()+":U"+hojaData.getLastRow()+"").setNumberFormats(formatos);
cell.setValues(formulas);
}
As you can see, the first function generates a GForm and set the destination to an existing ssheet. Then, with ordenarData() I want to order the sheets and get the name of the last tab. My problem is that if a link processForm() to the second function, they run at the same time and theres no time to generate the new tab, so the function does not work fine.
How can I set that the function ordenarData() runs only when processForm() has finished?
If you know any other way to get the name of the Tab which is generate with FormApp.create().setDestination it will be also okay for me.
Solve!
SpreadsheetApp.flush() works fine for me: getSheets() Google Apps Script multiple calls issue
I need a program for google script that tells me if I have an email address in my contacts or not.
I get many emails from vendors each day and many of them are completely new contacts. I need a program that makes a list of only the new contacts so I can decide which contact group to add them too.
Basic structure of what I'm looking for:
Will run this program every 5 minutes (I can set that up.)
-Checks emails with yellow or blue star for email address of recipient and sender. filters out my email address.
-Checks the email address against all my contacts. If the email address does not appear in my google contacts I need:
Email address, subject line, message text, additional thread message, additional thread message... in seperate columns in a spreadsheet.
I will then go through this information manually to decide which groups to put them in.
Thank you for your help!!!
UPDATE//////////////////////////////update.3
Here's where I'm at. This is working on and off. One time I run it it works, another time it doesn't. REally annoying. If anyone can see any glairing problems let me know. Especially hard to get the if sndr and if rcpnt to return false to run the rest of the program. I've tried about 20 ways!!!
//http://webapps.stackexchange.com/questions/9813/get-e-mail-addresses-from-gmail-messages-received
/////////////NESTED LOOP METHOD
function newEmailAddressList(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("NewEmails");
var range = sheet.getRange("A2:BA");
var addrA;
range.clearContent(); //May need to change this to my delete function to speed things up.
var contact = ContactsApp.getContacts();
for(var i = 0;i < contact.length;i++){
var addrA = [];
var start;
var addresses = contact[i].getEmails();
for(var j = 0;j < addresses.length;j++){
addrA.push(addresses[j].getAddress());
};
};
sheet.getRange('H1').setValue("List Created");
for (var i=0; i<50; i++){
var threads = GmailApp.getInboxThreads(0,50)[i];
var messages = threads.getMessages()[0];
var sndr = messages.getFrom().replace(/^.+<([^>]+)>$/, "$1"); //http://stackoverflow.com/questions/26242591/is-there-a-way-to-get-the-specific-email-address-from-a-gmail-message-object-in
var rcpnt = messages.getTo().replace(/^.+<([^>]+)>$/, "$1");
function contains(addrA, sndr) {
for (var i = 0; i < addrA.length; i++) {
if (addrA[i] === sndr) {
sheet.appendRow("Emails Match");
}else{
var dat = messages.getDate();
//var sndr = messages.getFrom();
//var rcpnt = messages.getTo();
var sub = messages.getSubject();
var msg = messages.getPlainBody();
var info = [dat,sndr,rcpnt,sub,msg];
sheet.appendRow(info); //appendRow only works with sheet class not range class
};
};
};
};
};
Well, I'll give you a start. And keep in my that I've never really worked with GmailApp in the past so all of this information is pretty much readily available to any who opens the code editor clicks on help and then clicks on API Reference. Everything you need to know about Google Scripts is right there organized about as well as it's ever been. They have really improved their documentation since the first time I've looked at it several years ago.
So I'm giving you some functions that will get all of your contact emails, your from emails from your inbox and and your emails.
function myContacts()
{
var s = '';
var br = '<br />';
var contact = ContactsApp.getContacts();
for(var i = 0;i < contact.length;i++)
{
var addrA = [];
var addresses = contact[i].getEmails();
for(var j = 0;j < addresses.length;j++)
{
s += addresses[j].getAddress() + br;
addrA.push(addresses[j].getAddress());
}
}
dispStatus('Contact Emails',s, 800, 400);
}
function MyFroms()
{
var threads = GmailApp.getInboxThreads();
var s = '';
for(var i = 0; i < threads.length; i++)
{
var msg = threads[i].getMessages();
for(var j = 0; j < msg.length;j++)
{
s += msg[j].getFrom() + '<br />';
}
}
dispStatus('My Messages', s , 800 , 400);
}
function MyMessages()
{
var threads = GmailApp.getInboxThreads();
var s = '';
for(var i = 0; i < threads.length; i++)
{
var msg = threads[i].getMessages();
for(var j = 0; j < msg.length;j++)
{
s += 'Message' + j+1 + '<br />';
s += msg[j].getFrom() + '<br />';
s += msg[j].getBody() + '<br />';
}
}
dispStatus('My Messages', s , 800 , 400);
}
function dispStatus(title,html,width,height)
{
var title = typeof(title) !== 'undefined' ? title : 'No Title Provided';
var width = typeof(width) !== 'undefined' ? width : 800;
var height = typeof(height) !== 'undefined' ? height : 400;
var html = typeof(html) !== 'undefined' ? html : '<p>No html provided.</p>';
var htmlOutput = HtmlService
.createHtmlOutput(html)
.setWidth(width)
.setHeight(height);
SpreadsheetApp.getUi().showModelessDialog(htmlOutput, title);
}
It's not a complete answer. But hopefully it will encourage you to jump in get your feet wet and exercise your mental muscles and bring us back at least a partially working skeleton of a program that we can help you get running.
I am trying to populate a google form with questions scraped from a google sheet. Currently when I run my code I am getting the questions created, but only 25% or so actually have the string, the rest are simply blank. The questions that appear correctly change every time I run the script. It is seemingly random.
function formPopulation() {
var ss = SpreadsheetApp.openById("--");
var sheet = ss.getSheetByName('Tracker');
var auditTool = ss.getSheetByName('Audit Tool');
var validatorInfo = ss.getSheetByName('Validator Info');
//Sheet Info
var rows = auditTool.getLastRow(); //Number of Rows
var columns = auditTool.getLastColumn(); //Number of Columns
var startRow = 1;
var startColumn = 1;
var dataRange = auditTool.getRange(startRow, startColumn, rows, columns);
//getRange(first row of data, first column of data, last row of data, last column of data)
var data = dataRange.getValues();
//Sets working range of script
var form = FormApp.openById("--");
var item = form.addListItem();
var entityName = "";
var arrayOfEntities = [];
var newEntity = '';
for (var i = 4; i < columns; i++) {
//4 because that is where entity names begin
entityName = data[i][2];
Logger.log('entityName: ' + entityName);
newItem = item.createChoice(entityName);
arrayOfEntities.push(newItem);
};
item.setTitle("Select Entity").setChoices(arrayOfEntities);
var requirement = "";
var arrayOfRequirements = [];
var newRequirement = '';
for (var j = 5; j < rows; j++) {
//5 because that is where Requirements begin
if (data[0][j] != null) {
requirement = data[0][j];
if (requirement != "" || requirment != null){
requirement = "question #" + j;
Logger.log('requirement: ' + requirement);
form.addMultipleChoiceItem().setTitle(requirement).setChoiceValues(['Complete', 'Incomplete']);
};
};
};
};
The first question is supposed to be a multiple choice item where each 'entity' is an option. The remainder of the questions are supposed to be whether each 'requirement' is marked complete or incomplete.
Here is the spreadsheet I am working from
you have a typo:
if (requirement != "" || requirment != null){
should be 'requirement'
Here in last forloop
requirement = "question #" + j;
Please verify, is it ok ? or you should use
requirement = "question #" + j + ' ' +data[0][j];
I have excel as an activeX object in javascript. I seem to be missing something with reards to how to interact with the object model from there. My watch window shows the value of the "Value" property of the range I am trying to pull data from as "undefined" when I try to assign "range.Value" to an array.
Unfortunately I am unable to update the outdated browsers on my machine at work so I cannot upload pictures.
My script:
function open_files(A, B, C)
{
var excel = new ActiveXObject("Excel.Application");
excel.Visible=true;
excel.DisplayAlerts = false;
var wbA = excel.Workbooks.Open(document.getElementById(A).value);
var wbB = excel.Workbooks.Open(document.getElementById(B).value);
var wbC = excel.Workbooks.Open(document.getElementById(C).value);
excel.EnableEvents = false;
excel.ScreenUpdating = false;
excel.Calculation = -4135 //xlCalculationManual enumeration;
var wb_collection = [wbA, wbB, wbC];
excel.Application.Run("'" + wbA.name + "'" + '!update_links');
var CLIN_list = [wbA.Sheets("Control Form").Range("B62:B141").value(1)]
for (i = 0; i = CLIN_list.length; i++)
{
if (CLIN_list(i) > 0)
{
var CLIN_list_count = i
}
}
var decrement_range_start = wbA.Sheets("Fee & Decrement Table").Range("AJ14")
//for (i = 0; i < 80; i++){
//Sheets("Fee & Decrement Table").Cells(decrement_range_start.column+i
// Model Setup for VBA
wbA.Sheets("CONTROL FORM").Activate
wbA.Sheets("CONTROL FORM").OLEObjects("TextBox21").Object.Text = wbB.fullname
wbA.Sheets("CONTROL FORM").OLEObjects("TextBox22").Object.Text = wbC.fullname
excel.Application.Run("'" + wbA.name + "'" + '!Run_JPO');
I found an answer on another forum. A Range cannot be assigned directly to a js array, it has to be converted. The line below works to fill my CLIN_list variable.
var CLIN_list = new VBArray(wbA.Sheets("Control Form").Range("B62:B141").value).toArray();
The function I'm struggling with is part of a script that creates folders and copies a certain spreadsheet into each folder 15 times and names them.
I have a loop to create the files and inside of it I have an if..then..else statement to use a certain name for the file if j>10 and another name (else) if it's not. It always names the file one name and doesn't seem to recognize the if/else statement to change the name if j is over 10.
Basically what I want is to name the file
var namedFileCopy = fileCopy.setName(newFileName);
if j is less than 10 and...
var _namedFileCopy = fileCopy.setName("_" + newFileName);
if j isn't less than 10.
I'm wondering if I need to add a break in there somewhere, but I'm not really familiar with how to use breaks.
This is the function that is giving me a problem, and you'll find all my code in the snippet following it.
function putFilesIntoFolders(arrayOfFolderNames,theFolderNames,cell) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getSheetByName('Sheet1');
var folderType = theSheet.getRange(2,1);
var cell = folderType.getValue();
var file = DriveApp.getFileById("ID");
var dest_folder = "";
var baseFileName = "",
newfile,
newFileName = "",
i=0,
j=0;
for (i=0;i<arrayOfFolderNames.length;i+=1) {
var source_folder = DriveApp.getFolderById("ID");
dest_folder = DriveApp.getFolderById(folderIds[i]);
Logger.log('dest_folder' + dest_folder);
baseFileName = arrayOfFolderNames[i];
for (j=1; j<16; j+=1) {
var newFileName = baseFileName + " " + cell + " " + j.toString();
var fileCopy = file.makeCopy();
var namedFileCopy = fileCopy.setName(newFileName);
var _namedFileCopy = fileCopy.setName("_" + newFileName);
if (j<10) {
dest_folder.addFile(namedFileCopy);
source_folder.removeFile(fileCopy);
} else {
dest_folder.addFile(_namedFileCopy);
source_folder.removeFile(fileCopy);
};
};
};
};
var folderIds = [];
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Data System Tool')
.addItem('Create Data System Folders', 'copyAndRenameTemplate')
.addToUi();
}
function copyAndRenameTemplate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getSheetByName('Sheet1');
var rangeOfFileNames = ss.getRange("B4:B");
var twoD_Array = rangeOfFileNames.getValues();
var arrayOfFileNames = twoD_Array.join().split(",");
var folderType = theSheet.getRange(2,1);
var cell = folderType.getValue();
Logger.log(folderType);
Logger.log(cell);
// throw new Error('This is not an error. This is just to abort javascript');
var fldrNamesRng = theSheet.getRange(4,1,theSheet.getLastRow()-3,1);
Logger.log('fldrNamesRng: ' + fldrNamesRng);
var folderNames = fldrNamesRng.getValues();
Logger.log('folderNames: ' + folderNames);
var oneD_FolderNames = folderNames.join().split(",");
Logger.log('oneD_FolderNames: ' + oneD_FolderNames);
makeTheFolders(oneD_FolderNames);
putFilesIntoFolders(oneD_FolderNames);
};
function makeTheFolders(theFolderNames,cell) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getSheetByName('Sheet1');
var folderType = theSheet.getRange(2,1);
var cell = folderType.getValue();
var i=0,
folderObj;
for (i=0;i<theFolderNames.length;i+=1) {
folderObj = DriveApp.createFolder(theFolderNames[i] + " " + cell);
folderIds.push(folderObj.getId())
};
};
function putFilesIntoFolders(arrayOfFolderNames,theFolderNames,cell) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getSheetByName('Sheet1');
var folderType = theSheet.getRange(2,1);
var cell = folderType.getValue();
var file = DriveApp.getFileById("ID");
var dest_folder = "";
var baseFileName = "",
newfile,
newFileName = "",
i=0,
j=0;
for (i=0;i<arrayOfFolderNames.length;i+=1) {
var source_folder = DriveApp.getFolderById("ID");
dest_folder = DriveApp.getFolderById(folderIds[i]);
Logger.log('dest_folder' + dest_folder);
baseFileName = arrayOfFolderNames[i];
for (j=1; j<16; j+=1) {
var newFileName = baseFileName + " " + cell + " " + j.toString();
var fileCopy = file.makeCopy();
var namedFileCopy = fileCopy.setName(newFileName);
var _namedFileCopy = fileCopy.setName("_" + newFileName);
if (j<10) {
dest_folder.addFile(namedFileCopy);
source_folder.removeFile(fileCopy);
} else {
dest_folder.addFile(_namedFileCopy);
source_folder.removeFile(fileCopy);
};
};
};
};
There's no issue about your loops, no need to break; out of them.
Your problem occurs in these lines of code:
var fileCopy = file.makeCopy();
var namedFileCopy = fileCopy.setName(newFileName);
var _namedFileCopy = fileCopy.setName("_" + newFileName);
The makeCopy() method will create a new file object, representing a new file in your Google Drive.
After that, you call the setName() method on the fileCopy object, which changes the name of the associated file. That's fine, except that you've assigned the return value of the method call to a new variable, namedFileCopy. You've now got two variables that reference the same object, because this method simply returns the original object for chaining. (Chaining is when we call multiple methods on on object in the same statement; it's only possible if each method returns the original object reference for the next method.)
Next, you call setName() on fileCopy again, assigning the return value to _namedFileCopy; that's the third reference to the exact same object. Oh, and that file - the file that all three object references point to - is now named with an underscore.
After this, your if..then..else is doomed to name the resulting file with an underscore. Actually... the file is already named with an underscore, from the third operation above.
Separate concerns some more; you've got to make a decision about a file name, so do that in isolation. Once you've completed that concern, you are ready to manipulate the file, which is a separate concern.
The resulting loop should look something like this:
for (i=0; i<arrayOfFolderNames.length; i++) {
var source_folder = DriveApp.getFolderById("ID");
dest_folder = DriveApp.getFolderById(folderIds[i]);
Logger.log('dest_folder' + dest_folder);
baseFileName = arrayOfFolderNames[i];
for (j=1; j<16; j++) {
// Determine the name for the new file
var newFileName = baseFileName + " " + cell + " " + j.toString();
// Prepend file name with underscore if j >= 10
if (j >= 10) newFileName = "_" + newFileName;
// Copy "file" and apply our newFileName, then move to new destination.
var fileCopy = file.makeCopy()
.setName(newFileName);
dest_folder.addFile(fileCopy);
source_folder.removeFile(fileCopy);
}
}
(I suspect this is what being said in Alvaz' answer.)
fileCopy.setName() changes the filename property of the file object. You should move your calls to fileCopy.setName() inside of your conditional branches.