Updating previous question with detailed example. I have a google sheet with three used columns (A: Event Title, B: Event Description, and D: Event Date). I want to create a new all day event on Google Calendar if none exists, and update the existing one if it already exists.
Here is the code I tried: I am getting the error: TypeError: Cannot read property 'setDescription' of undefined
function updatecal(){
var cal = CalendarApp.getCalendarById('c_lmsj3r1ogsaafhc68gfalstdco#group.calendar.google.com');
var data = SpreadsheetApp.getActive().getSheetByName("Data For Calendar");
const rows = data.getDataRange().getValues();
rows.forEach(function(row, index){
if (index === 0) return;
var eventdate = data.getRange(index, 4, 1, 1).getValue();
var eventtitle = data.getRange(index,1,1,1).getValue();
var eventdescription = data.getRange(index,2,1,1).getValue();
console.log(eventdate + eventtitle + eventdescription)
const eventdate1 = new Date(eventdate)
const events = cal.getEventsForDay(eventdate1);
if (events.lenght == 0){
var newevent = cal.createAllDayEvent(eventtitle,eventdate1,
{description: eventdescription});
} else {
ev = events[0];
ev.setDescription(eventdescription)
}
})
}
There is a typo in line 13 inside the if statement. You wrote lenght instead of length If you fix that the code runs just fine and the calendar events are created without any problem.
function updatecal(){
var cal = CalendarApp.getCalendarById('c_lmsj3r1ogsaafhc68gfalstdco#group.calendar.google.com');
var data = SpreadsheetApp.getActive().getSheetByName("Data For Calendar");
const rows = data.getDataRange().getValues();
rows.forEach(function(row, index){
if (index === 0) return;
var eventdate = data.getRange(index, 4, 1, 1).getValue();
var eventtitle = data.getRange(index,1,1,1).getValue();
var eventdescription = data.getRange(index,2,1,1).getValue();
console.log(eventdate + eventtitle + eventdescription)
const eventdate1 = new Date(eventdate)
const events = cal.getEventsForDay(eventdate1);
if (events.length == 0){ //The error was in this line
var newevent = cal.createAllDayEvent(eventtitle,eventdate1,
{description: eventdescription});
} else {
ev = events[0];
ev.setDescription(eventdescription)
}
})
}
References:
length()
Related
This is the Google Sheet, it can be copied: https://docs.google.com/spreadsheets/d/1ffIRGiGkiy5WFzSAvWNOY_3cqNXgTAOtO6o8vxS-BFU/edit?usp=sharing
The Function 'AddNewMembers' does not function, even if "isAdded == "No" it will not setValue(recruit_id)
function AddNewMembers(event){
event = {range: SpreadsheetApp.getActiveRange()}
CheckHandleSteamIDNotation(event)
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var recruitment_log = ss.getSheetByName('RL1');
var main_roster = ss.getSheetByName('Main Roster');
var isAdded = recruitment_log.getRange('R3').getValue();
if(isAdded == "No") {
var recruit_id = "'" + recruitment_log.getRange('J3').getValue();
main_roster.getRange('I100').setValue(recruit_id);
}
}
function CheckHandleSteamIDNotation(event)
{
let formSheet = SpreadsheetApp.getActiveSheet();
let header = formSheet.getRange(1,1,1,formSheet.getMaxColumns()).getValues();
let formRange = formSheet.getRange(formSheet.getLastRow(), 1, 1, formSheet.getMaxColumns());
let formValues = formRange.getValues();
for(let i = 0; i < header[0].length; i++)
{
if(header[0][i].includes("SteamID"))
{
formValues[0][i] = "'" + formValues[0][i];
}
}
formRange.setValues(formValues);
}
Since the provided script above contains var isAdded = recruitment_log.getRange('R3').getValue(); the value of R3 is currently set to "Yes" that is why the condition for the script below is not running.
if(isAdded == "No") {
var recruit_id = "'" + recruitment_log.getRange('J3').getValue();
main_roster.getRange('I100').setValue(recruit_id);
}
Try this modification:
function AddNewMembers(event) {
event = { range: SpreadsheetApp.getActiveRange() }
CheckHandleSteamIDNotation(event)
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var recruitment_log = ss.getSheetByName('RL1');
var main_roster = ss.getSheetByName('Main Roster');
//Gets all the data values on recruitment_log
var isAdded = recruitment_log.getRange(3, 1, recruitment_log.getLastRow(), recruitment_log.getLastColumn()).getValues();
//Gets the last row starting I17
var lastrow = main_roster.getRange(17, 9, main_roster.getLastRow() , 1).getValues().filter((x => x > 1)).length
//Sets the value on the last blank row
isAdded.map(x => x[17].toString().toLocaleLowerCase() == "no" ? "'" + main_roster.getRange(17 + lastrow,9).setValue(x[9]) : x)
}
I made modifications on your isAdded variable to the following to get the entire range of data on RL1 sheet.
var isAdded = recruitment_log.getRange(3, 1, recruitment_log.getLastRow(), recruitment_log.getLastColumn()).getValues();
This part of script was only used to get the current length of data for the New Operatives. Using .filter() method to filter out empty array elements, since getValues() gets blank cells if there is formatting applied on the spreadsheet.
var lastrow = main_roster.getRange(17, 9, main_roster.getLastRow() , 1).getValues().filter((x => x > 1)).length
Using ES6 .map() method to create a new array for the data that hasn't been added to the main roster sheet file.
isAdded.map(x => x[17].toString().toLocaleLowerCase() == "no" ? "'" + main_roster.getRange(17 + lastrow,9).setValue(x[9]) : x)
Screenshot:
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#description
for the last 3 days (my first 3 days of coding) i've been trying to code an script to get my google contacts that have certain keyword, it being "Catamarca", on their name and also to delete that keyword after they've been added to the spreadsheet leaving only their name.
I've been succesfull in all of this. But now i want to only run the script on an existing database, and only run it if the new contacts are not on the sheet already, and not write over the existing ones.
Here is my code so far:
function impContacts() {
// variables
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("GContacts");
var grupo = ContactsApp.getContactGroupById("http://www.google.com/m8/feeds/groups/email#gmail.com/base/6")
const contactos = grupo.getContacts();
const arraycontacts = [];
// get the last row on B with data on it
const lastRow = sheet.getLastRow();
const Avals = sheet.getRange("B2:B" + lastRow).getValues();
const Alast = lastRow - Avals.reverse().findIndex(c => c[0] != '');
Logger.log(Alast);
var rangeInteres = sheet.getRange(2, 1, Alast, sheet.getLastColumn()).getValues();
// look for contacts that has in their name the word "Catamarca", and save them with their id, name and phone.
contactos.forEach(contacto => {
var phone = ""
if (contacto.getPhones()[0]) {
phone = contacto.getPhones()[0].getPhoneNumber()
};
var name = ""
if (contacto.getFullName().match("Catamarca")){
name = contacto.getFullName();
};
var idcont = ""
if (name == rangeInteres.forEach(namme =>
{
oldName = namme[1];
Logger.log(oldName);}))
{
idcont =
rangeInteres.forEach(id => {
oldId= id[0];
Logger.log(oldId);})
}
else
{
idcont = contacto.getId().replace(/\D/g, '')
};
const datoscont = [idcont, name, phone];
arraycontacts.push(datoscont);
})
// save new contact
sheet.getRange(2, 1, arraycontacts.length, 3).setValues(arraycontacts);
// look for "catamarca"
range = sheet.getRange("B2:B")
var textFind = range.createTextFinder("Catamarca");
textFind.matchEntireCell(false);
textFind.ignoreDiacritics(true);
textFind.matchCase(false);
var textFound = textFind.findNext();
// Si encuentra coincidencia reemplazar por ""
if (textFound !== null) {
var vals = textFound.getValues();
textFind.replaceAllWith("");
}
}
What i need to keep the most is the ID of the existing contacts (the IDs are different from the ones coming from google contacts but the names are the same), because they are linked to an app created using AppSheet.
I believe there should be a way to accomplish this by editing this part of the code
var idcont = ""
if (name == rangeInteres.forEach(namme =>
{
oldName = namme[1];
Logger.log(oldName);}))
{
idcont =
rangeInteres.forEach(id => {
oldId= id[0];
Logger.log(oldId);})
}
else
{
idcont = contacto.getId().replace(/\D/g, '')
};
In my head and with my current knowledge, the code should be working, but it's not, I mean, it runs, but overwrites everything.
I have a loop in which I am calling rec_append() recursively, apparently the first pass alone works, then the loop stops.
I have an array of 4 elements going into that $.each loop but I see only the first element going into the function recursively. Help!
I switched it for a element.forEach but that gives me only the second element and I am stuck, is there a better solution to process a tree of elements? My array is a part of a tree.
var data = JSON.parse(JSON.stringify(result))
var graph = $(".entry-point");
function rec_append(requestData, parentDiv) {
var temp_parent_details;
$.each(requestData, function (index, jsonElement) {
if (typeof jsonElement === 'string') {
//Element construction
//Name and other details in the form of a : delimited string
var splitString = jsonElement.split(':');
var details = document.createElement("details");
var summary = document.createElement("summary");
summary.innerText = splitString[0];
details.append(summary);
temp_parent_details = details;
parentDiv.append(details);
var kbd = document.createElement("kbd");
kbd.innerText = splitString[1];
summary.append(' ');
summary.append(kbd);
var div = document.createElement("div");
div.className = "col";
details.append(div);
var dl = document.createElement("dl");
div.append(dl);
var dt = document.createElement("dt");
dt.className = "col-sm-1";
dt.innerText = "Path";
div.append(dt);
var dd = document.createElement("dd");
dd.className = "col-sm-11";
dd.innerText = splitString[2];
div.append(dd);
var dt2 = document.createElement("dt");
dt2.className = "col-sm-1";
dt2.innerText = "Type";
div.append(dt2);
var dd2 = document.createElement("dd");
dd2.className = "col-sm-11";
dd2.innerText = splitString[1];
div.append(dd2);
} else {
$.each(jsonElement, function (jsonElementArrIndx, jsonChildElement) {
rec_append(jsonChildElement, temp_parent_details); //Only 1 pass works, rest skip
});
}
});
}
rec_append(data, graph);
Sample data:enter image description here
I have a web app with one drop down list and 2 buttons. The drop down list get values from a sheet. The buttons write back in the sheet. The script I have works fine with that:
<script>
$(function() {
$('#txt1').val('');
google.script.run
.withSuccessHandler(updateSelect)
.getSelectOptions();
});
function updateSelect(opt)
{
var select = document.getElementById("sel1");
select.options.length = 0;
for(var i=0;i<opt.length;i++)
{
select.options[i] = new Option(opt[i],opt[i]);
}
}
function listS() {
const selectElem = document.getElementById('sel1')
const index = selectElem.selectedIndex;
if (index > -1) {
const e = document.getElementById("sel1");
const value = e.options[index].value;
const body = { index: index, value: value };
google.script.run.withSuccessHandler(yourCallBack).yourServerSideFunc(body);
}
}
document.getElementById("but1").addEventListener("click",listS);
function yourCallBack(response) {
}
</script>
In Java script:
function getSelectOptions()
{
var ss=SpreadsheetApp.openById('1onuWoUKh1XmvEAmKktwJekD782BFIru-MDA0omqzHjw');
var sh=ss.getSheetByName('Database');
var rg=sh.getRange(2,1,sh.getLastRow()-1,8);
var vA=rg.getValues();
var useremail = Session.getActiveUser().getEmail();
var opt=[];
for(var i=0;i<vA.length;i++)
{
if(vA[i][1] == "Pending Approval"){
if(vA[i][7]+"#xxx.com" == useremail || vA[i][7]+"#xxx.com" == useremail) {
opt.push(vA[i][3]+" REQ ID: "+vA[i][0]);
}
}
};
if (opt.length == 0) {opt.push("You do not have pending requests")};
return opt;
}
function doGet() {
var output = HtmlService.createHtmlOutputFromFile('list');
return output;
}
function yourServerSideFunc(body) {
var value = body["value"];
var ss = SpreadsheetApp.openById('1onuWoUKh1XmvEAmKktwJekD782BFIru-MDA0omqzHjw');
var sh = ss.getSheetByName('Database');
var rg=sh.getRange(1,1,sh.getLastRow()-1,4);
var vA=rg.getValues();
var str = "Approved";
for(var i=0;i<vA.length;i++)
{
if(vA[i][3]+" REQ ID: "+vA[i][0] == value) {
sh.getRange(i+1, 2).setValue(str);
}
};
return ContentService.createTextOutput(JSON.stringify({message: "ok"})).setMimeType(ContentService.MimeType.JSON);
Now I am trying to regenerate the drop down list values after the button is clicked. I tried to add
var output = HtmlService.createHtmlOutputFromFile('list');
return output;
in yourServerSideFunc(body) function to regenerate the HTML but does not work. I have tried to force a HTML refresh, but also did not work.
How can I easily re-trigger the generation of the drop down list items? Worst case scenario it is ok to refresh the whole page, but it should be simple to regenerate the drop down list since I have already the code for it.
I ended up with this work around.
function listS() {
const selectElem = document.getElementById('sel1')
const index = selectElem.selectedIndex;
if (index > -1) {
const e = document.getElementById("sel1");
const value = e.options[index].value;
const body = { index: index, value: value };
google.script.run.withSuccessHandler(yourCallBack).yourServerSideFunc(body);
//ADDED:
var select = document.getElementById("sel1");
select.options[index] = new Option("Approved! Please refresh","Approved! Please refresh");
selectElem.selectedIndex = index;
}
}
It does not really meet the original goal to refresh the list from the sheet. It would be great if someone else posted a solution to call the server function. I tried to add google.script.run.doGet() and similar, but it seems that it does not call the server side functions properly.
I want to customize an autocomplete function to Codemirror.
So I have build this code:
CodeMirror.commands.autocomplete = function (cm) {
var arrayTabNONDefault = new Array();
var stringaCampi = null;
var arrayTabellaCampo = null;
var textVal = cm.getValue();
textVal = textVal.toUpperCase();
var res = textVal.match("SELECT(.*)FROM");
if (res != null) {
stringaCampi = res[1];
arrayTabellaCampo = stringaCampi.split(",");
var nomeTab = null;
for (var i = 0; i < arrayTabellaCampo.length; i++) {
nomeTab = (arrayTabellaCampo[i].split(".")[0]).trim();
if (hintTables[nomeTab] == null)
hintTables[nomeTab] = new Array();
} //FINE FOR
} //FINE IF
CodeMirror.showHint(cm, CodeMirror.hint.sql, {
tables: hintTables
});
cm.on("beforeChange", function (cm, change) {
var before = cm.getRange({ line: 0, ch: 0 }, change.from);
var text = cm.getRange(change.from, change.to);
var after = cm.getRange(change.to, { line: cm.lineCount() + 1, ch: 0 });
if (before.indexOf("FROM") !== -1)
// alert("Ho scritto FROM");
console.log("before change", before, text, after);
});
cm.on("change", function (cm, change) {
var from = change.from;
var text = change.text.join("\n");
var removed = change.removed.join("\n");
var to = cm.posFromIndex(cm.indexFromPos(from) + text.length);
var before = cm.getRange({ line: 0, ch: 0 }, from);
var after = cm.getRange(to, { line: cm.lineCount() + 1, ch: 0 });
if (before.indexOf("FROM") !== -1)
console.log("after change", before, removed, text, after);
});
} //FINE ESTENSIONE
This is the content of hintTables
var hintTables = { "#T_TF_FilesList": ["FilesListHeaderID", "NumRecord", "FileTypeID", "FileID", "FilesListHeaderID", "NumRecord"],
"#T_TF_SelectedItems": ["EventHeaderID", "ItemType", "ItemID1", "ItemID2", "EventHeaderID", "ItemType", "ItemID1", "ItemID2"],
"#T_TFT_CacheSearchCriteriaHeaders": ["ID", "SyncDate", "FileTypeID", "CriteriaExpressionString", "CriteriaExpressionHash", "PageRecordsNumber", "PageNumber", "NumFiles"]
};
So I want that the system should propose a list of this table after I write FROM, or the system should to propose a list of stored procedures after I write EXECUTE.
It is possible to do this?
Are you trying to customize the SQL hint addon? If so, you should make changes inside sql-hint.js (under codemirror/addon/hint).
Basically what you should do is:
1.In your app.js (whatever js file for your main logic) call editor.showHint({hint: CodeMirror.hint.sql) on "change" event;
2.Inside sql-hint.js, return {list: hintTables, from: somePos, to: somePos} when the user types FROM or EXECUTE which can be detected by regular expression or inspecting the tokens at the line. I made up some code for your reference:
var cursor = editor.getCursor();
var tokenAtCursor = editor.getTokenAt(cursor);
if (tokenAtCursor.type == "FROM-and-EXECUTE")
return {list: hintTables,
from: CodeMirror.Pos(cur.line, tokenAtCursor.start),
to: CodeMirror.Pos(cur.line, tokenAtCursor.end)};
If I misunderstand your question and this answer is not helpful, please tell me and I will delete it.