getCalendarById where the ID is a String - google-apps-script

On a Google Spreadsheet there are several calendar IDs. Each is in Column B, and is in the format of "SuperLongStringGoesHere#group.calendar.google.com".
Things work when I manually enter the calendar ID instead of using a variable (the variable is calRngVal). When I use a variable, it returns null. How do I call getCalendarById using a variable?
var calRng = calids.getRange(a, 2);
var calRngVal = calRng.getDisplayValue();
var cal = CalendarApp.getCalendarById(calRngVal);
var ad = cal.getName();
Thanks in advance!
Here is a picture of the debugger:

You don't need to do anything special to access a calendar using a string value stored in a sheet. Both your approach and that mentioned in Simon's answer should work.
There must be something else going wrong. Are you sure you have permission to access all of the calendars listed in the sheet? If you don't have permission, getCalendarById will return null.
Wrap the code after your getCalendarById call in a try/catch block so that one "bad" calendar won't crash the script.
EG:
var calRng = calids.getRange(a, 2);
var calRngVal = calRng.getDisplayValue();
var cal = CalendarApp.getCalendarById(calRngVal);
try{
var ad = cal.getName();
//other code here
}catch(e){
Logger.log('Exception processing '+calRngVal+', row '+a+'. Message: '+e);
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
Also note that looping and fetching the ranges one by one is a slow way to do this, you could use getDataRange() to return all the data in the sheet in a two dimensional array, then just loop over the array. However I would fix the "null" problem before optimizing.

Are you sure the code is selecting a cell with a calendar id value in it? Try this:
var calRng = calids.getRange("A1:B");
var calRngVal = calRng.getValues();
var cal = CalendarApp.getCalendarById(calRngVal[1][1]);
var ad = cal.getName();
If it doesn't work, try using the logger sometime:
Logger.log(calRngVal);

Related

Inserting variable data into an existing Google Sheet in Google Scripts

I'm building a script that will ultimately take data from a csv file, populate a spreadsheet, use that spreadsheet to autofill a number of documents, and then automatically e-mail those documents to customers. It's also moving the documents from each time the script runs to a new folder with just that day's reports. I'm pretty new to Google Scripts, and this has been my learning project.
The steps I've got to work so far is the creation of documents for the spreadsheet with dummy data.
I ended up creating a second script to send e-mails which sends e-mails, but it is set up to look for all documents in the folder that are google docs, so it send a copy of every document to each customer. I thought that if I pointed the e-mail back to the original spreadsheet to grab just the correct document IDs (instead of the type) , I could only send customers the reports that belonged to them (all in PDF form). The step of creating a new folder and moving the documents into it afterwards works fine.
What I'm having an issue with is inputting data into my document ID column in the original spreadsheet. I have been able to watch it put the document ID of the first document into every row that has info to iterate over in the column, and then replace every row again with the second document's ID, etc.
I looked for ways to add data to a spreadsheet. Every method I've found so far involves creating a new column or row with new information from data within the spreadsheet. I need to put in data that I'm just now creating outside of the spreadsheet and then put it in the right place so I can point to it later.
I've gone over the methods within the documentation. It looks like .getCell.setvalue(variable) should work...if I could find a way to get the cell from the range (Which keeps showing me out of range).
function createDocument() {
var headers = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A1:P1');
var tactics = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A2:P');
var templateId = '1DU13OJHWyYnO5mLFovm97pWwXuU7ZTTDVJb2Mpdeebk';
for(var i = 0; i < tactics.values.length; i++){
var customer = tactics.values[i][0];
var pcname = tactics.values[i][1];
var date = tactics.values[i][2];
var virusvalue = tactics.values[i][3];
var cpuuse = tactics.values[i][4];
var ramuse = tactics.values[i][5];
var harddrive = tactics.values[i][6];
var netuse = tactics.values[i][7];
var downtime = tactics.values[i][8];
var cpuperform = tactics.values[i][9];
var ramperform = tactics.values[i][10];
var harddiskperform = tactics.values[i][11];
var reccomend = tactics.values[i][13];
var custID = tactics.values[i][14];
var newdoc = tactics.values[i][15];
//Make a copy of the template file
var documentID = DriveApp.getFileById(templateId).makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
//rename the copied file
DriveApp.getFileById(documentID).setName('MCHA ' + customer + ' - ' + pcname);
Logger.log('value1 ' + documentID);
//THIS IS THE AREA I'M TRYING TO FIX
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
var cell = ss.getRange('P2:P').getCell([i], [15]);
cell.setValue(documentID);
SpreadsheetApp.flush();
Logger.log('value2 ' + documentID);
//This area has code to replace the tags in the document with values from the spreadsheet. I cut it for not being relevant.
}
}
Obviously defining the range is just going to fill and autofill each cell. That code worked when I tried it
I originally tried using the variable from my earlier list for the autofill, but I've since realized that shouldn't work at all.
I tried to set the range of cells and then set the current cell by the same notation ([i][15] etc.) which throws an 'Range not found' error. I feel like I must be missing some syntax.
A link to the folder with all the documents is below, if that helps.
https://drive.google.com/drive/folders/1sRhti3R8R-Cym0hr2S4XkAVn3wyBbSRu?usp=sharing
I may not have entirely understood the problem you are facing. But I can see the cause of the 'Range not found' error.
Your loop starts with a value of 0 for i. This causes the script to look for a range called P0 in the first iteration.
One way to avoid that is to try :
var cell = ss.getRange("P"+(i+1));
For code efficiency, I'd also suggest moving some of the code outside the loop. For example, the following line runs each time in the loop. But it could be defined once outside the loop and then the variable ss could be reused inside the loop.
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
Similarly, you could define the template file outside the loop and then sue it inside to make copies:
var templateFile = DriveApp.getFileById(templateId); // Outside the loop
And then inside the loop:
var documentID = templateFile.makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
Google apps script best practices suggests minimising calls to the spreadsheet, i.e. get the data in one call, process it, and then post the data to the sheet in one call. More here.
Hope this helps.

Script using the createCourses function using data from a sheet

I am creating a script using the createCourses function. The template code provided on google development support is where this began. Now I want to manipulate the template to pull data from a spreadsheet. Debuggin is giving me an error - "Invalid JSON payload received. Unknown name "name" at 'course': Proto field is not repeating, cannot start list." The data appears to be pulled but I can't figure out how to repeat the create function for all my courses.
Here is my code... I have replaced the sheets 'ID' on purpose!
function createCourses() {
var course;
course = Classroom.newCourse();
var ss = SpreadsheetApp.openById('ID');
course.name = ss.getRange("A2:A").getValues();
course.ownerId = ss.getRange("H2:H").getValues();
//course.id = "Bio10";
course = Classroom.Courses.create(course);
Logger.log('%s (%s)', course.name, course.id);
var list = Classroom.Courses.create();
Logger.log(create);
}
You don't say but I assume you are getting the error on the line var list = Classroom.Courses.create(); you're not passing any parameters to the .create() method.
To repeat the function you would just need to pull the range of data from the spreadsheet and use a for loop to iterate through the data and create your courses.

Google sheet not updating custom function return value

I am very new to Google Apps Script (as well as JavaScript, for that matter), but I have been trying to tinker with it for fun.
I have tried writing a script to fetch API price data in Google Sheets, but am finding that the returned value is not updating when re-evaluating the script in the same cell.
Below is a script to fetch bitcoin price data from Coinbase's API. The script parses the JSON response of the request, as is described here.
function getBTCPrice() {
var url = "https://api.coinbase.com/v2/prices/BTC-USD/spot";
var response = UrlFetchApp.fetch(url);
var jsonSpotPrice = response.getContentText();
var parseSpotPrice = JSON.parse(jsonSpotPrice);
var price = "$" + parseSpotPrice.data.amount;
return price
}
Now, if I type =getBTCPrice() in some cell, and then re-evaluate a few moments later, I get the same price; however, if I evaluate the script in a different cell, I get a different result.
I've read some stuff about Google caching values in cells, so that perhaps the script isn't evaluated because the value of the cell has not changed. Is this the case here? If so, is there a workaround?
Any help is greatly appreciated!
I finally figured it out! Instead of trying to call the custom function from an actual sheet cell (which apparently stores cached values), the trick is to call the function within a script.
Using my above script:
function getBTCPrice(url) {
var response = UrlFetchApp.fetch(url);
var jsonSpotPrice = response.getContentText();
var parseSpotPrice = JSON.parse(jsonSpotPrice);
var price = "$" + parseSpotPrice.data.amount;
return price;
}
You can then call this function from another script. Specifically, I was looking to assign the updated price to a cell. Below is an example, which assigns the price to the active spreadsheet, in cell A1:
function updatePrice(){
var a = getBTCPrice("https://api.coinbase.com/v2/prices/BTC-USD/spot");
SpreadsheetApp.getActiveSpreadsheet().getRange('A1').setValue(a);
}
You can then proceed to set an appropriate time trigger. And that's all there is to it!
Have a look at this answer on Refresh data retrieved by a custom function in google spreadsheet.
As the answerer says, the trick is to
My solution was to add another parameter to my script, which I don't even use. Now, when you call the function with a parameter that is different than previous calls, it will have to rerun the script because the result for these parameters will not be in the cache.
Vik
In addition of Vikramaditya Gaonkar answer, you can use a installable trigger to get a refresh result each minute.
function getBTCPrice(input) {
url = "https://api.coinbase.com/v2/prices/BTC-USD/spot";
response = UrlFetchApp.fetch(url);
var jsonSpotPrice = response.getContentText();
var parseSpotPrice = JSON.parse(jsonSpotPrice);
var price = "$" + parseSpotPrice.data.amount;
return price
}
function up(){
SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Math.random());
}
The parameter of getBTCPrice function is, in my case, cell A1 which is randomize each minute. For this, I create a installable trigger on up function
function up, time-driven, minute timer, every minute
I was also trying to make my custom function update, after searching I came up with the following function:
function updateFormulas() {
range = SpreadsheetApp.getActiveSpreadsheet().getDataRange();
formulas = range.getFormulas();
range.clear();
SpreadsheetApp.flush();
range.setValues(formulas);
}
The function above update all formulas of the spreadsheet. In my experience to make a custom function update I had to change its value, so I get all the data of the sheet, then I get the formulas and store them into a variable, then I clear their values and apply this change with "flush", finally I update the values I have just cleared with the formulas I have stored.
I created this function and in my case I have set the trigger for 1 minute to execute it, every minute all functions of the table are updated.
I hope this helps you.

Get a specific cell value in a Google Spreadsheet Script

I am trying to modify a little script to email myself the results of a Google Form.
The script is working in its basic form, but I can't seem to be able to retrieve the value from a cell and copy it in the subject field.
Here is what I am trying:
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var title = SpreadsheetApp.getActiveSheet().getRange(lastRow, 2).getValues();
var subject = "Todo List: " + title;
The script stops working though, when I do that :)
Any idea why? Thanks
Never tried this but looking at 'title' you have ...getValues() suggesting a collection of titles which you assign to a single variable what does getValues()[0] give you. Try indexing into that collection maybe..
getValues() will return a 2-dimensional Javascript array (so I think getValues()[0][0] should work); but as you are wanting to retrieve a single value from a single cell, you may as well just use getValue().
var title = SpreadsheetApp.getActiveSheet().getRange(lastRow, 2).getValue();

Writing a string of multiple date / time to a single cell

I have an array of a couple (the array is up to 10) date/time that I want to write to a spreadsheet using getRange().setValues(). I'm converting the array to a string and it looks correct in Logger.
[Mon Feb 02 14:01:00 GMT-06:00 2015, Tue Feb 02 01:00:00 GMT-06:00 2016, , , , , , , , ]
When I try to write the string to a single cell in a sheet:
target6.setValues(source_range6_values);
I get this error:
Incorrect range width, was 10 but should be 1 (line 84, file "Code")
Edited 4/28/2014 adding entire script:
/**
* Copies source range and pastes at first empty row on target sheet
*/
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
//changing the array values to a string
classDate.toString();
//Building a new variable with the string to be inserted below in the target sheet
var source_range6_values = classDate;
//source_range6_values.toString();
Logger.log(classDate[0]);
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Aadding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
To give a correct answer for your question, i guess i need to know how you get the value of source_range6_values.
One quick guess is you might want to use target6.setValue instead of target6.setValues since you want to write the data into one cell only...
A quick & dirty way would be to replace the commas(with spaces):
source = String(source_range6_values).replace("," , " ");
I've had fun with GAS and variables. Casting it as a String should let you use the string functions on it. If that doesn't work can you share a mock-up of your sheets so I can take a look?
edit:
I had to play around with it a bit, seems google's version of .replace() only replaces the first instance (and doesn't allow .replaceAll() ).
I edited your code starting on line 23:
//Getting the values of the array
var classDate = daterange.getValues().toString();
//Building a new variable with the string to be inserted below in the target sheet
//Google has bugs, .replace() seems to only replace the first instance
//-while {} loop replaces all of them
while (!classDate.equals(classDate.replace("," , " "))) { classDate = classDate.replace("," , " "); };
var source_range6_values = classDate;
All the dates are in one cell if you change only those lines (and no errors).
I appreciate the help you two have given me trying to answer this question. #swimmingwood fixed the actual capture of the data into a string, but it left commas and when I inserted it into the target sheet, it wrote it to multiple cells with an error. It did write to the sheet but the error had you use a CTRL-E (inside the taget sheet) to complete the insert and wrote them into separate cells.
#MickATX suggested the code to replace the commas in the string with a space, which would be fine, but apparently he discovered a Google scripting problem that would only allow for the first comma to be replaced and ignore the rest. Great knowledge never-the-less.
I ended up using a formula in an addition cell in the source sheet that looked like this:
=ArrayFormula(CONCATENATE(REPT(TEXT(H2:Q2,"mm/dd/yyyy hh:mm a")&CHAR(10),H2:Q2>0)))
This formula wrote all the date/time entries provided by the form entry into one cell of the source sheet and ONLY the number of entries (1-10). I then wrote that single cell to the target sheet via the script.
Thanks to #swimmingwood and #MickATX for trying to help me, both provided worthy knowledge.
I've read a couple of strange answers here...
If you write an 2D array to a sheet it will obviously be written accross multiple cells... commas are definitely not the issue but the nature of the object is.
Simply convert your array into a string using .toString() or .join() (the latter providing the advantage you can choose the separator to use) and setValue() (without S) at the place you want.
the commas you see in the logger are only typographic representation of array elements separators...
And, last point : the .join() or .toString() methods return new variables, they don't modify the original value so when you write classDate.toString(); you are not doing anything ...
you should write it like this :
classDateAsAString = classDate.toString();
finally your code :
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
var source_range6_values = classDate.join(' & ');// using & as separator for example
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Adding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
Now if you want to format the dates in a more civilized way, that should be handled a bit differently... let me know if you still need it / want it.