I've created the following code to run a manual script to change the day in multiple instances within a Google Slides presentation. If the day is Monday, the script changes it to Thursday. If it is Thursday, the other script changes it to Monday.
function futurebriefthurs() {
var currdate = new Date();
var daystochange = 3;
var newbriefday = new Date(currdate.getFullYear(), currdate.getMonth(), currdate.getDate() + daystochange);
var NextBrief = Utilities.formatDate(new Date(newbriefday), "GMT-7", "EEEEE");
var pattern = "\\b\\d{1,2}/\\d{1,2}/\\d{4}\\b";
var slides = SlidesApp.getActivePresentation().getSlides();
var slidesLength = slides.length;
for (var i = 0; i < slidesLength; i++) {
var shapes = slides[i].getShapes();
var shapesLength = shapes.length;
for (var j = 0; j < shapesLength; j++) {
if (shapes[j].getDescription() == "$NextBrief") {
var textRange = shapes[j].getText();
textRange.clear();
textRange.insertText(0, NextBrief);
}
}
}
}
function futurebriefmon() {
var currdate = new Date();
var daystochange = 4;
var newbriefday = new Date(currdate.getFullYear(), currdate.getMonth(), currdate.getDate() + daystochange);
var NextBrief = Utilities.formatDate(new Date(newbriefday), "GMT-7", "EEEEE");
var pattern = "\\b\\d{1,2}/\\d{1,2}/\\d{4}\\b";
var slides = SlidesApp.getActivePresentation().getSlides();
var slidesLength = slides.length;
for (var i = 0; i < slidesLength; i++) {
var shapes = slides[i].getShapes();
var shapesLength = shapes.length;
for (var j = 0; j < shapesLength; j++) {
if (shapes[j].getDescription() == "$NextBrief") {
var textRange = shapes[j].getText();
textRange.clear();
textRange.insertText(0, NextBrief);
}
}
}
}
What I would like to do is have a script that runs upon opening the presentation that checks the current day and makes the same changes automatically. We use this presentation on scheduled days so the need to change the day to other than Monday and Thursday is minimal.
At some point, I would like to be able to have the changed based on Google Calendar entries, but that is for another day. :-)
I think something like this will work.
You just need to run createOpenTrigger() when you install the code. I assumed everything else in the code was working.
function createOpenTrigger(){
var triggers=ScriptApp.getProjectTriggers();
var tA=[];
for(var i=0;i<triggers.length;i++){tA.push(triggers[i].getHandlerFunction());}
if(tA.indexOf('futureBrief')!=-1){
ScriptApp.newTrigger('futureBrief').forSpreadsheet(sheet).onOpen().create();
}
}
function futureBrief() {
var currdate = new Date();
var dtcA=[0,4,0,0,3,0,0];
var daystochange=dtcA[currdate.getDay()];
if(daystochange>0){
var newbriefday = new Date(currdate.getFullYear(), currdate.getMonth(), currdate.getDate() + daystochange);
var NextBrief = Utilities.formatDate(new Date(newbriefday), "GMT-7", "EEEEE");
var pattern = "\\b\\d{1,2}/\\d{1,2}/\\d{4}\\b";
var slides = SlidesApp.getActivePresentation().getSlides();
var slidesLength = slides.length;
for (var i = 0; i < slidesLength; i++) {
var shapes = slides[i].getShapes();
var shapesLength = shapes.length;
for (var j = 0; j < shapesLength; j++) {
if (shapes[j].getDescription() == "$NextBrief") {
var textRange = shapes[j].getText();
textRange.clear();
textRange.insertText(0, NextBrief);
}
}
}
}
}
Related
I'm importing a CSV from an email with the following code:
function RetrieveAttachment()
{
var threads = GmailApp.search("Report*")
var msgs = GmailApp.getMessagesForThreads(threads);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var today= new Date();today.setDate(today.getDate());
var today= Utilities.formatDate(today, "GMT+1", "dd.MM.yyyy");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Import");
for(var i = 0; i < msgs.length; i++)
{
for(var j = 0; j < msgs[i].length; j++)
{
var msgdate = Utilities.formatDate(new Date(msgs[i][j].getDate()), "GMT+1", "dd.MM.yyyy");
if(msgdate == today)
{
var attachments = msgs[i][j].getAttachments();
for(var k = 0; k < attachments.length; k++)
{
var attachmentName = attachments[k].getName();
var stringValue = attachmentName.search("*");
if(stringValue > -1)
{
var attachmentData = attachments[k].getDataAsString();
var parseCsv = Utilities.parseCsv(attachmentData, ",");
sheet.clearContents();
sheet.getRange(1,1, parseCsv.length, parseCsv[0].length).setValues(parseCsv);
}
}
}
}
}
}
Usually this works just fine, but with one specific csv I get via email only this two question marks are being written in the Spreadsheet:
I checked with the debugger and apparently the whole content of the csv is being read, however not correctly parsed:
I presume this is some coding problem, however I tried with the base64Encode/base64Decode utilities without any success.
Does someone have any clue on how to solve this?
The original CSV file was UTF-16 and tab separated, I solved it specifying the encoding and different separator:
var attachmentData = attachments[k].getDataAsString("UTF-16");
var parseCsv = Utilities.parseCsv(attachmentData, "\t");
Here the new Code
function RetrieveAttachment()
{
var threads = GmailApp.search("Report*")
var msgs = GmailApp.getMessagesForThreads(threads);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var today= new Date();today.setDate(today.getDate());
var today= Utilities.formatDate(today, "GMT+1", "dd.MM.yyyy");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Import");
for(var i = 0; i < msgs.length; i++)
{
for(var j = 0; j < msgs[i].length; j++)
{
var msgdate = Utilities.formatDate(new Date(msgs[i][j].getDate()), "GMT+1", "dd.MM.yyyy");
if(msgdate == today)
{
var attachments = msgs[i][j].getAttachments();
for(var k = 0; k < attachments.length; k++)
{
var attachmentName = attachments[k].getName();
var stringValue = attachmentName.search("*");
if(stringValue > -1)
{
var attachmentData = attachments[k].getDataAsString("UTF-16");
var parseCsv = Utilities.parseCsv(attachmentData, "\t");
sheet.clearContents();
sheet.getRange(1,1, parseCsv.length, parseCsv[0].length).setValues(parseCsv);
}
}
}
}
}
}
I've implemented a script in my google sheet that grabs gcal calendar info and displays it in various tabs of the sheet. However, the end date of each event as displayed in the sheet is one day LONGER than what is displayed in the calendar itself. Code below. Through logging, I can't figure out why its happening. Is there something to do with the formatting of the date? Any help would be appreciated!
function populateAllTabs() {
var id = "[MY CAL ID HERE]"; // id is currently set to bookings calendar. NB: Takes a string!
var cal = CalendarApp.getCalendarById(id);
var startPeriod = new Date('January 1, 2020');
startPeriod.setHours(0, 0, 0, 0);
var endPeriod = new Date(startPeriod);
endPeriod.setDate(endPeriod.getDate() + 365); // looks for all events in the range one year
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){// loop over all tabs in the spreadsheet
var sheet = ss.getSheets()[n];// look at every sheet in spreadsheet
var name = sheet.getName();//get name
if(name != 'List'){
var gig = sheet.getRange(1,1);
var gigName = gig.getValue();
var events = cal.getEvents(startPeriod, endPeriod, {search:gigName});
// find the title of each event in the calendar
var eventTitles = [];
for (var i = 0; i < events.length; i++) {
eventTitles.push([events[i].getTitle()]);
}
// find the start date of each event in the calendar
var starttime = [];
for (var i = 0; i < events.length; i++) {
starttime.push([Utilities.formatDate(events[i].getStartTime(), "GMT", "MM/dd/yy")]);
}
// find the end date of each event in the calendar
var endtime = [];
for (var i = 0; i < events.length; i++) {
endtime.push([Utilities.formatDate(events[i].getEndTime(), "GMT", "MM/dd/yy")]);
}
var cell = sheet.getRange("B3");
cell.setValue(starttime + ' - ' + endtime);
}
}
}
This works for me. No problem with the dates
function populateAllTabs() {
var cal = CalendarApp.getCalendarById('id');
var startyear=2019;
var startPeriod = new Date(startyear,0,1);
var endPeriod = new Date(startyear+1,1,1);
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
for(var n=0;n<shts.length;n++){
var sheet=shts[n];
var name=sheet.getName();
if(name!='List'){
var gigName = sheet.getRange(1,1).getValue();
var ev=cal.getEvents(startPeriod, endPeriod, {search:gigName});
var gigs=[]
for (var i=0;i< ev.length;i++) {
gigs.push([ev[i].getTitle(),Utilities.formatDate(new Date(ev[i].getStartTime()),Session.getScriptTimeZone(),"MM/dd/yy"),Utilities.formatDate(new Date(ev[i].getEndTime()),Session.getScriptTimeZone(),"MM/dd/yy")]);
}
if(gigs) {
sheet.getRange(3,2,gigs.length,gigs[0].length).setValues(gigs);
}
}
}
}
I'm trying to create a script that will allow me to search for e-mails and their attachments by label name or no user labels. If in searchForLabels is more than one entry, the script does not work.
function searchLabels(){
//if there is more than one entry here, the script does not work
var searchForLabels =
[
'has:nouserlabels',
'label:Test1'
];
for (var l = 0; l < searchForLabels.length; l++) {
var threads = GmailApp.search('in:inbox newer_than:4d' + searchForLabels);
var msgs = GmailApp.getMessagesForThreads(threads);
Logger.log(searchForLabels)
if (searchForLabels == 'has:nouserlabels'){
for (var i = 0 ; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var message = msgs[i][j];
var from = message.getFrom();
var subject = message.getSubject();
var getAttachments = message.getAttachments();
var body = message.getPlainBody();
var getTo = message.getTo();
Logger.log(subject)
for (var k = 0; k < getAttachments.length; k++) {
var attachment = getAttachments[k];
var content = attachment.getContentType();
//rest of my code
}
}
}
}
}
}
I made two little changes over your code. After testing it, it works as you requested.
The first one is on the var threads = GmailApp.search('in:inbox newer_than:4d ' + searchForLabels[l]); line. I used the l iterator on the searchForLabels array. I used it because, reading the context of the line, it seems appropriate. This change will iterate over the tags. Forgive me if this was not your original intention.
The second change is required for the script to work. I commented out the if (searchForLabels == 'has:nouserlabels') {, because it will never be true if the searchForLabels array has more than one element. This is the error that you detected. Please, notice how the correspondent } is commented out too.
This is the final and working version of the script:
function searchLabels() {
//if there is more than one entry here, the script does not work
var searchForLabels = [
'has:nouserlabels',
'label:Test1'
];
for (var l = 0; l < searchForLabels.length; l++) {
var threads = GmailApp.search('in:inbox newer_than:4d ' + searchForLabels[l]);
var msgs = GmailApp.getMessagesForThreads(threads);
Logger.log(searchForLabels)
//if (searchForLabels == 'has:nouserlabels') {
for (var i = 0; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var message = msgs[i][j];
var from = message.getFrom();
var subject = message.getSubject();
var getAttachments = message.getAttachments();
var body = message.getPlainBody();
var getTo = message.getTo();
Logger.log(subject)
for (var k = 0; k < getAttachments.length; k++) {
var attachment = getAttachments[k];
var content = attachment.getContentType();
//rest of my code
}
}
}
//}
}
}
I want to add that you can use the Gmail API on Apps Script to accomplish your request in a simple way. In particular, you can use Users.messages LIST to list all the mails with your selected tags. There you can see an example (click over JavaScript) where a function is ready to make a search. To enable this API you shall follow the guide for activating advanced services. Do not hesitate to ask for more clarifications or any question.
function searchLabels() {
var searchForLabels = [
'has:nouserlabels',
'label:Test1'
];
for (var l = 0; l < searchForLabels.length; l++) {
var threads = GmailApp.search('in:inbox newer_than:4d ' + searchForLabels[l]);
var msgs = GmailApp.getMessagesForThreads(threads);
Logger.log(searchForLabels)
if (searchForLabels[l] == 'has:nouserlabels') { //limits emails to those without tag
//if (searchForLabels[l] == 'label:Test1') { //or to those with tag
for (var i = 0; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var message = msgs[i][j];
var from = message.getFrom();
var subject = message.getSubject();
var getAttachments = message.getAttachments();
var body = message.getPlainBody();
var getTo = message.getTo();
Logger.log(subject)
for (var k = 0; k < getAttachments.length; k++) {
var attachment = getAttachments[k];
var content = attachment.getContentType();
//rest of my code
}
}
}
}
}
}
I have a google app script that collects information about Gmail messages and then pastes it into a google sheet. Trouble is it doesn't get ALL of the messages. It only picks up the first one of each thread. I feel like I am missing something to loop through each thread? Any suggestions?
function getMail(){
var myspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var mysheet = myspreadsheet.getSheetByName("Sheet3");
var start = 0;
var max = 99;
var count =0;
var row = mysheet.getLastRow()+1
var maxDate = mysheet.getRange("B1").getValue()
while(count < 4)
{
var threads = GmailApp.getInboxThreads(start , max);
var messages = GmailApp.getMessagesForThreads(threads);
var froms = [];
messages.get
for(var i = 0; i < threads.length; i++)
{
var msgDate = messages[i][0].getDate();
if(msgDate>maxDate){
froms.push([messages[i][0].getDate(),messages[i][0].getFrom(),messages[i][0].getSubject(),messages[i][0].getPlainBody()]);
}
}
if(froms.length>0){
mysheet.insertRows(2, froms.length)
mysheet.getRange(2,1,froms.length,4).setValues(froms);
}
start = start + 100;
count++;
}
}
Your current script is only grabbing messages[i][0], the first message in that group for the thread. Instead you need to loop through all of the messages using two for loops, as you can see in the script below I use messages[i][j].
function getMail() {
var mySpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var mySheet = mySpreadsheet.getSheetByName("Sheet3");
var start = 0;
var max = 99;
var count = 0;
var maxDate = mySheet.getRange("B1").getValue();
while(count < 4) {
var threads = GmailApp.getInboxThreads(start, max);
var messages = GmailApp.getMessagesForThreads(threads);
var froms = [];
for(var i = 0; i < messages.length; i++) {
for(var j = 0; j < messages[i].length; j++) {
var msgDate = messages[i][j].getDate();
if(msgDate > maxDate) {
froms.push([msgDate,messages[i][j].getFrom(),messages[i][j].getSubject(),messages[i][j].getPlainBody()]);
}
}
}
if(froms.length > 0) {
mySheet.insertRows(2, froms.length);
mySheet.getRange(2, 1, froms.length, 4).setValues(froms);
}
start = start + 100;
count++;
}
}
Notable changes:
removed var rows because it wasn't used anywhere in the script.
changed first for loop to run for messages.length rather than
threads.
added another for loop to loop through every message in
messages[i].
you were getting messages[i][0].getDate() twice, so I just used the variable already defined for adding to the array.
minor grammatical/spacing changes for consistency across script.
I'm working with a set-list for a cover band in which I have listed all of our songs by decade on separate sheets, including a full set list on its own sheet. When we work on new songs, I color those cells green. I would like to see those same songs highlighted green wherever they may appear on the other sheets whenever I change color (onEdit, that is).
Here's what I've got so far pieced together from other sources:
function replace() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activecell = ss.getActiveSheet().getActiveCell().getA1Notation();
var activecellvalue = ss.getActiveSheet().getActiveCell().getValue();
var activecellcolor = ss.getActiveSheet().getActiveCell().getBackground();
var allsheets = ss.getSheets();
var s = 0;
var cells = [];
if (activecellvalue != 'Yes'|'No' && activecellcolor == '#00ff00'){
for (var s=0; s < allsheets.length; s++) {
var allcells = allsheets[s].getDataRange().getValues();
for (var i=0; i < allcells.length; i++) {
for (var j=0; j < allcells[i].length; j++) {
if (allcells[i][j].toString().match(activecellvalue) == activecellvalue) {
var newcells = allsheets[s].getRange(i+1,j+1).getA1Notation();
cells.push(newcells);}
}
}
allsheets[s].getRange(newcells).setBackground('#00ff00');
// cells.push(newcells);
Logger.log(newcells);
Logger.log(allsheets);
Logger.log(cells)
}
}
}
The "Yes" and "No" refer to cells containing messages in the negative or affirmative as to whether or not we have finished a song.
I have been able to get the script to color the same text in different cells in different sheets, but something still needs to be fixed which is allowing the same cell values in EVERY sheet to be highlighted, as well.
Thank you in advance for your help!
EDIT:
I have a working (albeit, seemingly inefficient) code that will seek out the text of an active cell elsewhere and change its cell color:
function colorchange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activecell = ss.getActiveSheet().getActiveCell().getA1Notation();
var activecellvalue = ss.getActiveSheet().getActiveCell().getValue();
var activecellcolor = ss.getActiveSheet().getActiveCell().getBackground();
var allsheets = ss.getSheets();
var s = 0;
var name = allsheets[s].getName();
if (activecellvalue != 'Yes'|'No' && activecellcolor == '#00ff00'){
for (var s=0; s < allsheets.length; s++) {
var cells = allsheets[s].getDataRange().getValues();
for (var i=0; i < cells.length; i++) {
for (var j=0; j < cells[i].length; j++) {
if (cells[i][j].toString().match(activecellvalue) == activecellvalue) {
var check = cells[i][j]
var newcells = allsheets[s].getRange(i+1,j+1).getA1Notation();
allsheets[s].getRange(newcells).setBackground('#00ff00');
}
}
}
}
}
else if (activecellvalue != 'Yes'|'No' && activecellcolor == '#ffffff'){
for (var s=0; s < allsheets.length; s++) {
var cells = allsheets[s].getDataRange().getValues();
for (var i=0; i < cells.length; i++) {
for (var j=0; j < cells[i].length; j++) {
if (cells[i][j].toString().match(activecellvalue) == activecellvalue) {
var check = cells[i][j]
var newcells = allsheets[s].getRange(i+1,j+1).getA1Notation();
allsheets[s].getRange(newcells).setBackground('#ffffff');
}
}
}
}
}
}
The problem is getting this script to trigger on a change to background color. According to this, such a feature is not yet possible. Can anyone provide a potential solution?
UPDATE:
Working solution. Triggered by a drop-down menu selection.
function songStatus()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = ss.getDataRange();
var curCell = sheet.getActiveCell();
var curCol = curCell.getColumn();
var curRow = curCell.getRow();
var allSheets = ss.getSheets();
var songs = [];
var status = sheet.getRange(curRow, 3).getValue();
var genre = sheet.getRange(curRow, 11).getValue();
if (status == "Now") {
sheet.getRange(curRow, 1).setBackground('#f884ff');
sheet.getRange(curRow, 3).setBackground('#f884ff');
var song = sheet.getRange(curRow, 1).getValue().toString();
for (var s=7; s < allSheets.length; s++) {
var row = allSheets[s].getDataRange().getLastRow();
var col = allSheets[s].getDataRange().getLastColumn();
var cells = allSheets[s].getDataRange().getValues();
for (var i=0; i < row; i++) {
for (var j=0; j < col; j++) {
if (cells[i][j].toString().match(song) == song) {
allSheets[s].getRange(i+1,j+1).setBackground('#f884ff');
}
}
}
}
}
else if (status == "Next")
{
sheet.getRange(curRow, 1).setBackground('#f2a2a2');
sheet.getRange(curRow, 3).setBackground('#f2a2a2');
var song = sheet.getRange(curRow, 1).getValue().toString();
for (var s=7; s < allSheets.length; s++) {
var row = allSheets[s].getDataRange().getLastRow();
var col = allSheets[s].getDataRange().getLastColumn();
var cells = allSheets[s].getDataRange().getValues();
for (var i=0; i < row; i++) {
for (var j=0; j < col; j++) {
if (cells[i][j].toString().match(song) == song) {
allSheets[s].getRange(i+1,j+1).setBackground('#f2a2a2');
}
}
}
}
}
}
Unfortunately, onEdit trigger only fires when an "Edit" is made by the user. However, you are right, changing the color is not considered as an edit. I would suggest a slightly different approach that would work in such a scenario. Instead of tracking your new songs by highlighting them with green and having the sheet run a script to detect if the color has been changed for that cell and then running a script to find that same song in other sheets, I'd suggest either tying your Yes/No drop-down to the onEdit function to fire the color change and the other scirpt to search for that song in other sheets, or making a new New/Old drop-down list to do that for you. Here's a small pseudo-code to do that:
function onEdit()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = ss.getDataRange();
var curCell = sheet.getActiveCell();
var curCol = curCell.getColumn();
var curRow = curCell.getRow();
var allSheets = ss.getSheets();
//Assuming your New/Old column in E
var status = sheet.getRange(curRow, 6).getValue();
if (status == "New")
{
sheet.getRange(curRow, 1, 1, range.getLastColumn()).setBackgroundRGB(230,240,220);
//Assuming your song name is in Column B
var song = sheet.getRange(curRow, 3).getValue();
for (var i=0; i<allSheets.length; i++)
{
var sRange = allSheets[i].getDataRange();
var sData = sRange.getValues();
var sRows = sRange.getLastRow();
for (var j=0; j<sRows; j++)
{
if (sData[j][1] == song)
{
allSheets[i].getRange(j+1, 1, 1, sRange.getLastColumn()).setBackgroundRGB(230,240,220);
}
}
}
}
}
Another advantage of having a code like this is that if your sheet evolves to say, track all the songs that were Old, New, In-progress, On hold or any different statuses that you could think of, and each of these had to be tracked by a color code at a glance, it's much easier to add else-if statements to do that for you.
Hope this helps!