I am making a query in google app script.
I am trying to do a grouping query, but it doesn't come out
var spreadsheetId_2 = '1Z6_d0CaY38VvfPaWi0-5XRRpitKFmy3gMJsHNiMIpiK8[';
var targetSheet_2 = 'Actividades';
var query_2 = 'select C where K = "WHITE" GROUP BY C ';
var ss_2 = SpreadsheetApp.openById(spreadsheetId_2);
var sheetId_2 = ss_2.getSheetByName(targetSheet_2).getSheetId();
var url_2 = "https://docs.google.com/spreadsheets/d/" + spreadsheetId_2 + "/gviz/tq?gid=" + sheetId_2 + "&tqx=out:csv&tq=" + encodeURIComponent(query_2);
var res_2 = UrlFetchApp.fetch(url_2, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
var row_2 = Utilities.parseCsv(res_2.getContentText());
var maximo_2=row_2.length;
for (var i = 1; i < maximo_2; i++) {
var pers =row_2[i][0].toString();
}
What should change?
You want to retrieve the values using GROUP BY of the query.
You want to know the reason that the values are not returned.
Modification points:
I think that in your query, an error occurs. By this, no values are returned. So for example, how about including count(C) as follows.
select C,count(C) where K = "WHITE" GROUP BY C.
Modified script:
When your script is modified, please modify as follows.
From:
var query_2 = 'select C where K = "WHITE" GROUP BY C ';
var ss_2 = SpreadsheetApp.openById(spreadsheetId_2);
var sheetId_2 = ss_2.getSheetByName(targetSheet_2).getSheetId();
var url_2 = "https://docs.google.com/spreadsheets/d/" + spreadsheetId_2 + "/gviz/tq?gid=" + sheetId_2 + "&tqx=out:csv&tq=" + encodeURIComponent(query_2);
var res_2 = UrlFetchApp.fetch(url_2, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
var row_2 = Utilities.parseCsv(res_2.getContentText());
To:
var query_2 = 'select C,count(C) where K = "WHITE" GROUP BY C'; // Modified
var ss_2 = SpreadsheetApp.openById(spreadsheetId_2);
var sheetId_2 = ss_2.getSheetByName(targetSheet_2).getSheetId();
var url_2 = "https://docs.google.com/spreadsheets/d/" + spreadsheetId_2 + "/gviz/tq?gid=" + sheetId_2 + "&tqx=out:csv&tq=" + encodeURIComponent(query_2);
var res_2 = UrlFetchApp.fetch(url_2, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
var row_2 = Utilities.parseCsv(res_2.getContentText());
row_2.shift(); // Added
Reference:
Group By
Related
I want to replace the values in the row when I edit a message on my telegram bot chat
How can I make use of the message_id to locate the row of values and directly replace it when the message has been edited?
function doPost(e) {
var contents = JSON.parse(e.postData.contents);
var id = contents.message.from.id;
var name = contents.message.chat.first_name;
var text = encodeURIComponent(contents.message.text);
var textstring = text.toString().replaceAll("%20", " ").replaceAll("%0A", "\n");
var messageid = contents.message.message_id
var dateNow = new Date;
var dd = dateNow.getDate();
var mm = dateNow.getMonth() + 1;
var yyyy = dateNow.getFullYear()
var hhmmss = dateNow.getHours() + ":" + dateNow.getMinutes() + ":" + dateNow.getSeconds();
var formatDate = dd + "/" + mm + "/" + yyyy + " " + hhmmss;
if (textstring.startsWith('/')) {
console.log("Command Line");
var answer = "[Command in development]";
sendMessage(id, answer);
}
else {
var answer = "Added: " + text;
sendMessage(id, answer);
sheet.appendRow([messageid, formatDate, id, name, textstring]);
}
}
First row is the initial message and second row is the intended outcome of the edited message
Outcome.jpg
You get all the ids and find the array index. Then if it is found you insert "over" the row. Else append:
function doPost(e) {
var contents = JSON.parse(e.postData.contents);
var id = contents.message.from.id;
var name = contents.message.chat.first_name;
var text = encodeURIComponent(contents.message.text);
var textstring = text.toString().replaceAll("%20", " ").replaceAll("%0A", "\n");
var messageid = contents.message.message_id
var dateNow = new Date;
var dd = dateNow.getDate();
var mm = dateNow.getMonth() + 1;
var yyyy = dateNow.getFullYear()
var hhmmss = dateNow.getHours() + ":" + dateNow.getMinutes() + ":" + dateNow.getSeconds();
var formatDate = dd + "/" + mm + "/" + yyyy + " " + hhmmss;
if (textstring.startsWith('/')) {
console.log("Command Line");
var answer = "[Command in development]";
sendMessage(id, answer);
}
else {
var answer = "Added: " + text;
sendMessage(id, answer);
const ids = sheet.getRange(1, 1, sheet.getLastRow()).getValues().flat()
const index = ids.indexOf(messageid)
const dataArray = [messageid, formatDate, id, name, textstring]
//If the id is not found then it returns -1. So this updates. Arrays are 0 index to we need to + 1 to get the right row.
if (index >= 0) {
sheet.getRange(index + 1, 1, 1, dataArray.length).setValues([dataArray])
//Else insert.
} else {
sheet.appendRow(dataArray)
}
}
}
I want my function to activate on specific sheet only. But I really do not know how to write. I have like 14 sheets. And anytime one of the sheets' values are changed. It automatically sends out an email which I do not want. I want my function to send email only based on the sheet I want. (I have add the trigger manually on my current project trigger).
function CheckbudgetAUD() {
for(var i=2;i<1000; i++){
var num1 = i;
var r = 4;
var department = "AUD";
var transactionIDRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,14);
var transactionID = transactionIDRange.getValue();
var monthRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,12);
var month = monthRange.getValue() ;
var costdescriptionRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,3);
var costdescription = costdescriptionRange.getValue();
var costRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,4);
var cost = costRange.getValue();
var actualbudgetRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,4);
var actualbudget = actualbudgetRange.getValue();
var ApprovalRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,18);
var Approval = ApprovalRange.getValue();
var purposeRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,7);
var purpose = purposeRange.getValue();
var timeRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,1);
var time = timeRange.getValue();
var CEOsapprovalRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,19);
var CEOsapproval = CEOsapprovalRange.getValue();
var addinformationRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,5);
var addi = addinformationRange.getValue();
var thisisdoneRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,30);
var thisisdone = thisisdoneRange.getValue();
var BlankRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,2);
var Blank = BlankRange.getValue();
//Emails Range
var email1Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Forecasted budget").getRange("F18");
var email1address = email1Range.getValue();
var email2Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Forecasted budget").getRange("F21");
var email2address = email2Range.getValue();
var email3Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,6);
var email3address = email3Range.getValue();
// Messages Range
var message1 = "Your request has been " + Approval + "d." + "\n" + "\n" + "Transaction ID: " + transactionID;
var message = "For the " + month + ", " + department + "'s " + costdescription + ' budget exceeded the limit'+ '\n'+ '\n'+ "CURRENT BUDGET APPLICATION INFORMATION" + "\n" + "COST: "+ cost + '\n' + "Transaction ID: "+ transactionID +'\n' + "Cost item: " + costdescription + "\n" + "Purpose: "+ purpose + "\n" + "Department Name: " + department + "\n" + "Timestamp: " + time + "\n" + "additional information: " + addi+ "\n" + 'Click the link below to approve'+ '\n' + 'https://docs.google.com/forms/d/e/1FAIpQLSduX3ol31Ddy3klEpynlO33wprEivAr-e9BL7fZ6Th-JQgjZA/viewform';
var subject = 'Exceeded the budget limit for current month';
var subject1 = 'Form Approval/Decline';
if ( Blank !== "" && thisisdone == "Apple"){
MailApp.sendEmail(email3address, subject1, message1);
}
//Over budget email
if ( Approval == "Decline" && thisisdone == "Apple"){
if ( cost < 200000 ) {
MailApp.sendEmail(email1address, subject, message);}
else if ( cost >= 200000 ) {
MailApp.sendEmail(email2address, subject, message);
}
}
}
}
I am also basically sending out email to 3 different parties at the same time in one function. Just FYI.
You might like to consider this sort of approach. You will find it to run much faster. Although there may be some corrections you will want to make. I noticed some duplication in the range assignments which are reflected in my assignments as well. Keep in mind that column number start at one and array indices start at zero.
function CheckbudgetAUD() {
var department = "AUD";
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(department)
var fsh=ss.getSheetByName("Forcasted budget");
var vA=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();
for(var i=0;i<vA.length; i++){
var transactionID = vA[i][13];
var month = vA[i][11];
var costdescription = vA[i][2];
var cost = vA[i][3];
var actualbudget=vA[i][3];
var Approval=vA[i][17];
var purpose=vA[i][6];
var time=vA[i][0];
var CEOsapproval=vA[i][18];
var addi=vA[i][4];
var thisisdone=vA[i][29];
var Blank=vA[i][1];
var email1address = fsh.getRange("F18").getValue();
var email2address = fsh.getRange("F21").getValue();
var email3address = vA[i][5];
var message1 = "Your request has been " + Approval + "d." + "\n" + "\n" + "Transaction ID: " + transactionID;
var message = "For the " + month + ", " + department + "'s " + costdescription + ' budget exceeded the limit'+ '\n'+ '\n'+ "CURRENT BUDGET APPLICATION INFORMATION" + "\n" + "COST: "+ cost + '\n' + "Transaction ID: "+ transactionID +'\n' + "Cost item: " + costdescription + "\n" + "Purpose: "+ purpose + "\n" + "Department Name: " + department + "\n" + "Timestamp: " + time + "\n" + "additional information: " + addi+ "\n" + 'Click the link below to approve'+ '\n' + 'https://docs.google.com/forms/d/e/1FAIpQLSduX3ol31Ddy3klEpynlO33wprEivAr-e9BL7fZ6Th-JQgjZA/viewform';
var subject = 'Exceeded the budget limit for current month';
var subject1 = 'Form Approval/Decline';
if (Blank!=="" && thisisdone=="Apple"){
MailApp.sendEmail(email3address, subject1, message1);
}
if (Approval=="Decline" && thisisdone=="Apple"){
if (cost<200000) {
MailApp.sendEmail(email1address, subject, message);
}else if( cost >= 200000 ) {
MailApp.sendEmail(email2address, subject, message);
}
}
}
}
If you're attempting to run something like this from onEdit() then you will want to limit which sheets that you want it to run on. For example consider the event object to be represented by an e then
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!='AUD')return;
This will limit the script to running only for sheet AUD.
I have a code I use to send email reminders based on dates in a spreadsheet. Most of the sheets I use it for are simple and under 100 lines of data. In the past I have used individual variables for each cell but I am looking for a way to find all instances of a value in a column (D), in this case, it is a city code ("SEA"), and return the row number, which I then want to take and use in a var to make it act like a vlookup.
ex: var empname = ss.getRange("A"+ rownumber).getValue();
Which should return column A of whichever row has "SEA" in it.
The challenge then is to be able to go down the column and do the same for each row that has "SEA" in it.
This is a one-line version of what I have done in the past.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = spreadsheet.getSheetByName("Active");
var empnum = ss.getRange("A8").getValue();
var empfirstname = ss.getRange("C8").getValue();
var emplastname = ss.getRange("B8").getValue();
var emplocation = ss.getRange("D8").getValue();
var test = ss.getRange("H8").getValue();
if (test === -1 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.' + '\n'
} else if (test === 0 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.' + '\n'
} else if (test === 1) {
var message = "";
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
The H column will present a -1, 0 or 1 depending on the date in the G column to determine if someone is due to take a test or not. I then want to send an email with the Employee Number, First Name and Last Name for each employee at the SEA branch. This has worked for me in the past but I have 230+ lines of employees and don't want to create that many variables. My solution is to get the row number of each row with a "SEA" location and use that as a variable in a getRange call. Not sure exactly how that would work, or if it even would. If there is a better solution, I am amenable to that.
Requirement:
Send an email when a row contains "SEA" and column H matches -1 or 0.
Solution:
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//find all occurrences of "SEA" and push range to array
var search = "SEA";
var ranges = ss.createTextFinder(search).findAll();
var message = ''; //pre-define email body
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) { //make sure message is not blank
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}
Explanation:
Okay, so I've made quite a few changes to your original script, if there's anything I've missed please let me know, I'm happy to explain it.
First of all, I've changed the script so that we're using textFinder, this is so that we can search through your sheet to find your desired pattern "SEA". This then pushes all of the ranges to an array that we are looping through in the for loop.
When looping through the ranges, we can grab all of the data in the row (e.g. values[0][0] is the data in column A). This is then pushed to var message to build the email body in the same format you specified in your question.
I've tried to keep the variable names alike to the ones in your original script to make it easier to understand which parts of the script are essentially the same as your original. Also there are comments throughout the script that should help you understand what it's doing at each point.
References:
textFinder Documentation
Update:
To scan only a certain column, try the code below instead. I've defined var range separately so that we can use textFinder on that range rather than the whole sheet.
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//define range to search
var lastRow = ss.getLastRow();
var range = ss.getRange(1, 4, lastRow); //range for column D
//find all occurrences of "SEA" in column D and push range to array
var search = "SEA"
var ranges = range.createTextFinder(search).findAll();
var message = '';
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}
We have a transportation request form that is used to assign vehicles. It also populates a calendar to keep up with which vehicles are being used on which day. Issue, if the time and date of the "return details" aren't after the "departure details" it throws an error and the calendar item creation function won't run.
ex. 12am is an issue for some. They may put...depart 1/20 4pm, return 1/20 12am. Which is actually before departure. Other times they simply put the wrong dates from being in a hurry.
I would like to check that part of the submission, if there is an error send and edit url link in an email back to the requestor with instructions to fix.
Any help on this would be appreciated.
function createCalEvent(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var columnIndex = headers[0].indexOf(calendarColumnName);
var data = sheet.getDataRange().getValues();
var form = FormApp.openById(formURL);
var emailAddress = "transportation#.net";
var myCal = CalendarApp.getCalendarById('transportation#.net');
for(var i = startRow-1; i < data.length; i++) {
if(data[i][0] != '' && data[i][columnIndex] == '') {
var timestamp = Utilities.formatDate(data[i][0], 'CST', 'MMMM dd, yyyy h:mm a');
var staffName = data[i][1];
var departureDate = Utilities.formatDate(data[i][2],'CST', "EEEE, MMMM dd, yyyy");
var departureTime = Utilities.formatDate(data[i][3], 'CST', "h:mm a");
var returnDate = Utilities.formatDate(data[i][4],'CST', "EEEE, MMMM dd, yyyy");
var returnTime = Utilities.formatDate(data[i][5],'CST', "h:mm a");
var destination = data[i][6];
var groupAttending = data[i][7];
var reasonForTrip = data[i][8];
var teacherInCharge = data[i][9];
var specialRequest = data[i][10];
var vehicleOneAssigned = data[i][11];
var vehicleTwoAssigned = data[i][12];
var originalEmail = data[i][13];
var numberOfPassengers = data[i][14];
var requestorEmailAddress = data[i][15];
var pdfUrlLink = data[i][40];
var editURL = data[i][41];
if(pdfUrlLink != ''){
var title = staffName + '-' + destination + '-' + vehicleOneAssigned;
var descriptionList = 'Group Attending: ' + groupAttending + '<br> Number of Passengers: ' + numberOfPassengers + '<br> Reason For Trip: ' + reasonForTrip + '<br> Teacher In Charge: ' + teacherInCharge + '<br> Special Request: ' + specialRequest + '<br> Request Link: ' + pdfUrlLink;
var event = myCal.createEvent(title,
new Date(departureDate + ' ' + departureTime),
new Date(returnDate + ' ' + returnTime),
{description: descriptionList});
var eventID = event.getId();
Logger.log('Calendar Calendar Event ID: ' + eventID);
sheet.getRange(i+1, columnIndex+1).setValue(eventID);
}
}
}
}
I'm trying to invoke the following:
var range5 = SpreadsheetApp.getActiveSheet().getRange(range1:range3);
where range1 = "C590" (string value) and range3 = "O594"
(string value) (found previously in the script)
But I get the error:
Missing ) after argument list
for the range5 line. Is there a way to pass the two strings as the range?
The questioner has a problem with this line of code
var range5 = SpreadsheetApp.getActiveSheet().getRange(range1:range3);
This line of code would have worked:
var range5 = SpreadsheetApp.getActiveSheet().getRange(range1+":"+range3);
The main thing here is how the variables are added/assigned to the command, and how the separator is added between the variables.
There are at least four variations for getRange but the most basic is "getRange ('cell/range address')". As shown above, it IS possible to join the variables and a separator to create a valid range.
But there are other options. In this scenario, the most obvious is getRange(row, column, numRows, numColumns). This expects integer parameters but getRow and getColumn can be used to break down each of the strings.
The code below is an example of managing the ranges as strings.
Each string is used separately to create the parameters of the specific range.
Step#1 - The string for range1 is used in getRange. This permits...
Step#2 and #3 - get the row and column integers for the range.
Step#4 - Repeat for range3
Step#5 - Now we can get range5 using "getRange (row, column, num rows, num columns)". The values for this are supplied/calculated from steps#1 thru 4.
function so_52759685() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range1 = "C590";
var rangeone = sheet.getRange(range1);
var rangeonerow = rangeone.getRow();
var rangeonecolumn = rangeone.getColumn();
Logger.log("RANGE1: " + range1 + " - row = " + rangeonerow + ", and the column number = " + rangeonecolumn); // DEBUG
var range3 = "O594";
var rangethree = sheet.getRange(range3);
var rangethreerow = rangethree.getRow();
var rangethreecolumn = rangethree.getColumn();
Logger.log("RANGE3: " + range3 + " - row = " + rangethreerow + ", and the column number is " + rangethreecolumn); //DEBUG
var range5 = sheet.getRange(rangeonerow, rangeonecolumn, rangethreerow - rangeonerow + 1, rangethreecolumn - rangeonecolumn + 1)
var range5Rows = range5.getNumRows();
var range5Columns = range5.getNumColumns();
Logger.log("RANGE5: " + range5.getA1Notation() + " - Number of rows = " + range5Rows + ", and number of columns" + range5Columns);
}
Alternatively, one might use a named range.
The same process applies except that:
1 - range1 and range3 are Named Ranges, assigned to C590 and O594 respectively.
2 - Instead of declaring the string literals, we use GetNamedRange.
function so_5275968502() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var rangeone = ss.getRangeByName("range1");
Logger.log("range one is " + ss.getRangeByName("range1").getA1Notation()); //DEBUG
var rangeonerow = rangeone.getRow();
var rangeonecolumn = rangeone.getColumn();
Logger.log("RANGE1: " + rangeone.getA1Notation() + " - row = " + rangeonerow + ", and the column number = " + rangeonecolumn); //DEBUG
var rangethree = ss.getRangeByName("range3");
Logger.log("range three is " + ss.getRangeByName("range3").getA1Notation()); //DEBUG
var rangethreerow = rangethree.getRow();
var rangethreecolumn = rangethree.getColumn();
Logger.log("RANGE3: " + rangethree.getA1Notation() + " - row = " + rangethreerow + ", and the column number is " + rangethreecolumn); //DEBUG
var range5 = sheet.getRange(rangeonerow, rangeonecolumn, rangethreerow - rangeonerow + 1, rangethreecolumn - rangeonecolumn + 1)
var range5Rows = range5.getNumRows();
var range5Columns = range5.getNumColumns();
Logger.log("RANGE5: " + range5.getA1Notation() + " - Number of rows = " + range5Rows + ", and number of columns" + range5Columns); //DEBUG
}