How to convert Date and Time with mailmerge google slides from sheets as it is in cell - date

I have created a table with which I can record our check in times of our employees with the help of a generated Qr code in each line.The data in the table is generated as slides and converted into pdf. For this I use a script that I got to work with your help and it works. Here I would like to thank you especially #tanaike.
My problem is that the date and time are not copied to the slides to be generated as indicated in the cell but completely with Central European time and I added in the script to look in column if its empty to generate the slide. If it's not empty don't do anything. As I said everything is working except this two things.
I must confess I did not try to correct it somehow because I had already shot the script and I made some here despair. It would be really great if you write me the solutions and I can take them over. I will share the spreadsheet with you and the screenshot with ae and time. Thanks for your time and effort to help people like us; we are really trying.

As another approach, when I saw your question, I thought that if your Spreadsheet has the correct date values you expect, and in your script, you are retrieving the values using getValues, getValues is replaced with getDisplayValues(), it might be your expected result.
When I saw your provided sample Spreadsheet, I found your current script, when your script is modified, how about the following modification?
From:
var sheetContents = dataRange.getValues();
To:
sheetContents = dataRange.getDisplayValues();
Note:
When I saw your sample Spreadsheet, it seems that the column of the date has mixed values of both the string value and the date object. So, if you want to use the values as the date object using getValues, please be careful about this.
Reference:
getDisplayValues()
Added:
About your 2nd question of I mean that when a slide has been generated, the script saves the link from the slide in column D if the word YES is in column L. How do I make the script create the slide if there is JA in the column L and there is no link in column D. is a link in column D, the script should not generate a slide again. Thus, the script should only generate a slide if column D is empty and at the same time the word JA is in column L., when I proposed to modify from if (row[2] === "" && row[11] === "JA") { to if (row[3] == "" && ["JA", "YES"].includes(row[11])) {, you say as follows.
If ichanged as you descripted if (row[3] == "" && ["JA", "YES"].includes(row[11])) { i got this error. Syntax error: Unexpected token 'else' Line: 21 File: Code.gs
In this case, I'm worried that you might correctly reflect my proposed script. Because when I tested it, no error occurs. So, just in case, I add the modified script from your provided Spreadsheet as follows. Please test this.
Modified script:
function mailMergeSlidesFromSheets() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
sheetContents = dataRange.getDisplayValues(); // Modified
sheetContents.shift();
var updatedContents = [];
var check = 0;
sheetContents.forEach(function (row) {
if (row[3] == "" && ["JA", "YES"].includes(row[11])) { // Modified
check++;
var slides = createSlidesFromRow(row);
var slidesId = slides.getId();
var slidesUrl = `https://docs.google.com/presentation/d/${slidesId}/edit`;
updatedContents.push([slidesUrl]);
slides.saveAndClose();
var pdf = UrlFetchApp.fetch(`https://docs.google.com/feeds/download/presentations/Export?exportFormat=pdf&id=${slidesId}`, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob().setName(slides.getName() + ".pdf");
DriveApp.getFolderById("1tRC505IWtTj8nnPB7XyydvTtCJmOb6Ek").createFile(pdf);
// Or DriveApp.getFolderById("###folderId###").createFile(pdf);
} else {
updatedContents.push([row[3]]);
}
});
if (check == 0) return;
sheet.getRange(2, 4, updatedContents.length).setValues(updatedContents);
}

function todaysDateAndTime() {
const dt = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM:dd:yyyy");
const tm = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"HH:mm:ss");
Logger.log(dt);
Logger.log(tm);
}

Related

Conditional formatting for dates

I'm trying to come up with a simple conditional format formula for highlighting cells that have a date that is greater than three months older than today's date. It seems though that the "Date is before" option only gives a few options, none of them seem to allow what I'm looking for. Is there a custom formula that could accomplish this?
Edit: attaching a snip of the column in question:
Formula :
=DAYS(now(),B2)>90
Go to the custom formula in the conditional formating rules and use this:
=DATEDIF(A1,TODAY(),"D")<90
try:
=1*C2>DATE(YEAR(TODAY()), MONTH(TODAY())+3, DAY(TODAY()))
also make sure you have valid dates and not plain text dates. you can test this with ISDATE formula
You can use Apps Script and a Custom Menu in order to solve your issue with setting the color in the cell depending on the date. Go to Tools->Script Editor and paste this code:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Check Difference', 'dateDifference')
.addToUi();
}
function dateDifference(){
var sheet = SpreadsheetApp.getActiveSheet().getActiveRange(); // Get the selected range on the sheet
var dates = sheet.getValues(); // Get the values in the selected range
var oneDay = 1000*60*60*24;
var row = 1;
var re = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/; // This will help you to check if it's really a date
dates.forEach(function(el){ // Iterate over each value
if(typeof el[0] == 'object'){ // Check if it's really a date
var gmtZone = el[0].toString().split(" ")[4].split(":")[0]; // Take the date's GMT
var dateFormatted = Utilities.formatDate(el[0], gmtZone, "dd/MM/yyyy"); // Format the date
if(re.test(dateFormatted)){ // Test if it's the right format
// This part will calculate the difference between the current date and the future date
var futureDateMs = new Date(el[0]);
var todayDateMs = (new Date()).getTime();
var differenceInMs = futureDateMs - todayDateMs;
var differenceInDays = Math.round(differenceInMs/oneDay);
if(differenceInDays >= 91.2501){ // Test if the difference it's greater to 91.2501 days (3 motnhs)
sheet.getCell(row, 1).setBackground("#00FF00"); // Set the color to the cell
}
row++;
}
}
});
Save it by clicking on File->Save.
Then you can select a range in a column and click on Custom Menu->Check Difference as you can see in the next image:
As you can see, you will get the desired result:
Notice
It's really important to be careful with what you consider to be a "month", I mean how many days you are going to take into consideration. In my code, I took Google's suggestion of 1 = 30.4167.
Docs
These are other Docs I read to be able to help you:
Utilities.formatDate()
Working with Dates and Times.
I hope this approach can help you.

What is the easiest way to import excel/google sheet spreadsheet to a Cloud Firestore database?

I need to import a large table of data into my database in one go. I currently have it as an Excel file but I am happy to copy it to Google sheets etc.
So far I've added a few entries manually directly via cloud firestore.
Is there a solution already out there to achieve this?
I think the easiest way to export table data into Firestore is to use a Google Apps Script Library (for Google Sheets).
Step 1
Make a Copy of THIS example Google Spreadsheet I created as an example
Step 2
From the menu of YOUR copy of the Example Google Spreadsheet from step 1, click Tools > Script Editor. This should open up the example Google App Script associated with the example spreadsheet.
Step 3
Follow the Steps for installing this library and then update the script with the following:
email
key
projectID
These variables are generated by going to the Google Service Accounts page. This will require that you already have a Firebase or Google Cloud account setup. I won't repeat all the steps that are already iterated in in the aforementioned Github writeup. Just follow them carefully, and realize that the private_key is THE ENTIRE KEY starting with -----BEGIN PRIVATE KEY-----\n, EVERYTHING in between, and ending with \n-----END PRIVATE KEY-----\n
Step 4
Insert a page on your spreadsheet that contains your data, and EDIT the script to use your new sheet name and your data. I have HEAVILY commented the script so it's pretty clear what almost every line of code is doing. For those of you that just want to peek at the Google App Script that's behind this spreadsheet, here's the code:
// Note this Script uses an external library as per this page:
// https://github.com/grahamearley/FirestoreGoogleAppsScript
// This solution requires a Google Spreadhseet and a Firebase Account
// FOLLOW THE INSTRUCTIONS ON THAT GITHUB REPO TO SETUP NEEDED API KEYS!!!
//Global Variables
const ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the active "workbook"
const sheet = ss.getSheetByName('Restaurants'); // CHANGE TO YOUR SHEET NAME
const headerRowNumber = 1; // If you have more than one row for your header, then change this value to number of header rows
// If you want to mark modified cells, then set up a trigger for the following function:
// Edit > Current Project Triggers > (+ Add Trigger) > On Edit Spreadsheet etc
function onEdit(e) {
var cell = ss.getActiveCell(); //This will also effectively get our row
var dataRange = sheet.getDataRange(); //This checks for all rows/columns with data
var modifiedCol = dataRange.getLastColumn()-1; //Our "modified" column should be the second to last
if (cell.getColumn() < modifiedCol && cell.getRow() > headerRowNumber) { //If we edit any cells to the left of our modified column and below our header...
var celltoMark = sheet.getRange(cell.getRowIndex(),modifiedCol) //Get the R/C cordinates of cell to place modified time
celltoMark.setValue(new Date()); //write timestamp to that cell
}
};
// This will parse any comma separated lists you create in any of your fields (useful for search words, or attributes, etc)
function listToArray(list) {
var ogArray = list.split(","); //Input is a comma separated list
let trimmedArr = ogArray.map(string => string.trim()); //Let's strip out the leading/trailing whitespaces if any
return trimmedArr; //return the cleaned array
}
function writeToFireStore() {
const email = 'sheets#yourprojectid.iam.gserviceaccount.com'; // CHANGE THIS!!!
const key = '-----BEGIN PRIVATE KEY-----\nYOURPRIVATEKEY\n-----END PRIVATE KEY-----\n'; // CHANGE THIS!!!
const projectID = 'yourprojectid'; // CHANGE THIS!!!
var firestore = FirestoreApp.getFirestore(email, key, projectID);
const collection = "MySpreadsheetData"; // Name of your Firestore Database "Collection"
var dataRange = sheet.getDataRange().offset(headerRowNumber, 0, sheet.getLastRow() - headerRowNumber); //this is your data range
var data = dataRange.getValues(); // this is an array of your datarange's values
var lastCol = dataRange.getLastColumn(); // this is the last column with a header
var newDoc = {}; // Instantiate your data object. Each one will become the data for your firestore documents
// r = row number in this case
for (let r = 0; r <= dataRange.getLastRow(); r++) {
//Logger.log("R = ",r);
var cellMod = dataRange.getCell(r+1, lastCol-1);
var cellFS = dataRange.getCell(r+1, lastCol);
var cellModVal = cellMod.getValue();
var cellFSVal = cellFS.getValue();
//
// IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS!!!
// Well, read the line below...
if (r > 2) break; //Comment Out this line after you're done testing otherwise you'll write all your rows to firestore after every run
newDoc[r] = {
name : data[r][1],
category : data[r][2],
cuisine : data[r][3],
address: {
add1: data[r][4],
add2: data[r][5],
city: data[r][6],
state: data[r][7],
zip: data[r][8]
},
tel: data[r][9],
searchterms: listToArray(data[r][10]) //Let's turn a csv list into an array
}
// For the sake of efficiency and to save $, we WON'T create documents that have already been created...
// ...and we won't update documents that have a fireStore Timestamp that's newer than a Modified Timestamp
// If there's not firestore timestamp in our spreadsheet, then let's create firestore document and update firestore stamp:
if (!cellFSVal) {
var now = new Date(); //Generate timestamp right now
try {
firestore.createDocument(collection + "/" + data[r][0], newDoc[r]); // To Use Your Own Document ID
//Now let's insert a timestamp in our Firestore TS column of the sheet so we know it's been added to Firestore
cellFS.setValue(now);
Logger.log("Row ",r,"(",data[r][1],") is NEW and was added to FireStore Successfully");
} catch (e) {
Logger.log("Error: ",e," : Document with same name already existed in Firestore.");
}
}
//var if FS Timestamp exists but, the modified time stamp is greater, let's update the Firstore Document
else if ((cellFSVal) && (cellModVal > cellFSVal)) {
try {
firestore.updateDocument(collection + "/" + data[r][0], newDoc[r]);
//Now let's insert a timestamp in our Firestore TS column of the sheet so we know it's been updated to Firestore
cellFS.setValue(now);
Logger.log("Row ",r,"(",data[r][1],") updated/edited.");
} catch (e) {
Logger.log("Error: ",e," : Document existed, we tried updating it, but jack shit happened.");
}
}
else {
Logger.log("Row ",r,"(",data[r][1],") Already in Firestore & hasn't been modified. Skipped.");
}
}
}
Step 5
Once your script is modified to your needs, it's time to run the script. Simply save it (File > Save), then choose the function "writeToFireStore" from the "Select function" dropdown selector in the menu bar (in between the icon of the bug, and the lightbulb), then hit the PLAY icon (to the left of the bug icon). At this point, you will likely be prompted to accept permissions to run the script, (which you need to accept if you want to run the script). Once you've accepted the permissions, then run the "writeToFireStore" function again if it hasn't already run, and voila!
NOTES:
I created a function that automatically writes a Modified Timestamp to the second to last column in the target sheet, and when you run the function, writes a Firestore Timestamp (so you know which rows have been successfully exported to Firestore). This way, if you run the firestore function again, and you haven't changed the data on your sheet, it won't bother updating the database with the same data (and will save you money and/or server resources). For this functionality to work, you must setup project Triggers (which is explained inside the script in the comments).

Unable to set a javascript date object to a cell hidden by filter

I have a script which checks certain conditions to send reminders emails or sms to my clients. the only issues I'm finding is that if I try to write a cell that is hidden by a filter, the script executes but the data is not changed in any way.
I'll write a short version of the whole script:
function test(){
var nowTime = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var lastrow =sheet.getLastRow();
var lastcol =sheet.getLastColumn();
var fullData =cell.offset(0, 0,lastrow-
cell.getRow()+1,lastcol).getValues();
var cell = sheet.getRange("A2");
var i=0;
while (i<fullData.length){
var reminderType =0;
var row = fullData[i];
if (row[0] == 1) {sendreminder();cell.offset(i, 2).setValue(new Date());}
}
}
if for example the first column has hidden all the rows with 1 the script executes and sends all the reminders but ignore the setvalue(), if the rows are visible it works perfectly.
One solution can de to remove the filter but would be very annoying since we use the filter a lot and the script is triggered by time every 10 minutes so I would be working on the sheet and suddenly the filter get removed to run the script.
I have tried with cell.offset, getrange etc.. without any success ... Ideas?
EDIT: The problem seems to be only if I try to write a date if (row[0] == 1) {cell.offset(i, 1).setValue(new Date());}
For instance I'm writing another information (a number) in a different column and that cell gets updated.
The rest remain the same
here is a test sheet I created:
https://docs.google.com/spreadsheets/d/1-FNDGmvCc8nRFTG65Sj9L2RhGn8R3DtwR3llwBG5-FA/edit#gid=0
For now I added this code to save the state of the filter and reestablish it at the end. It's not really elegant and still add a lot of chances of something being messed up or the script failing but it's the less invasive solution I came out with. Does someone has a better one?
// at the beginning
if (sheet1.getFilter()){
var filterRange= sheet1.getFilter().getRange();
var filterSetting = [];
var i=0;
while (i<filterRange.getNumColumns()-1) {filterSetting[i]= sheet1.getFilter().getColumnFilterCriteria(i+1);sheet1.getFilter().removeColumnFilterCriteria(i+1);i++; }
}
// At the end
if (sheet1.getFilter()){
var i=0;
while (i<filterRange.getNumColumns()-1) {if (filterSetting[i]) sheet1.getFilter().setColumnFilterCriteria(i+1, filterSetting[i]);i++; }
}
Given the fact the problem is not writing in the hidden row as it was pointed out, the solution is to use the formatdate to format the value before writing it in the cell.
Not ideal either since regional formattings might prevent the script from working on different accounts but sure better solution than disabling the filters.
here is the code i needed to add:
var nowTime = new Date();
var timeZone = Session.getScriptTimeZone();
var nowTimeFormatted = Utilities.formatDate(nowTime, timeZone, 'MM/dd/yyyy HH:mm:ss');
any idea on how to improve this script or to solve the original problem without workarounds is appreciated :)

Utilities.formatDate() in Google Apps Script outputs previous date (e.g. input 25.05.2015 -> output 24.05.2015)

I have a problem with Google Docs' Utilities.formatDate() function.
I have a spreadsheet that contains all of the orders we place in the lab. When an order is delivered our lab manager enters the delivery date in the relevant cell in such a spreadsheet, in the following format: dd.MM.yyyy.
I created a script that, provided certain conditions, will email whoever placed that order alerting them that the order has been delivered on that particular date. Here is the code:
function DeliveryAlerts() {
try {
var email_dict = {"Y":"Y#Z.com"}
var spreadsheet = SpreadsheetApp.openById("ABC");
SpreadsheetApp.setActiveSpreadsheet(spreadsheet);
var sheet = spreadsheet.getSheetByName("Orders");
var values = sheet.getRange("A2:Q251").getValues();
var bgcolours = sheet.getRange("A2:Q251").getBackgrounds();
for(var i=0;i<=249;i++)
{
var j = i + 2;
if (values[i][16]=="Yes" && values[i][11]!="" && bgcolours[i][16]!="#b8b8b8")
{
var email_address = email_dict[values[i][13]];
var cur_date = Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy");
var message = "Hello there,\n\nYour order of " + values[i][4] + " has been delivered on "+ cur_date +".\n\nBest wishes";
var subject = "Delivery alert";
MailApp.sendEmail(email_address, subject, message,{replyTo:"abc#abc.com", name:"ABC"});
sheet.getRange("Q"+j).setBackground("#b8b8b8");
}
}
} catch (err) {
MailApp.sendEmail("abc#abc.com", "Delivery Alerts Script in Order Master List", err);
}
}
I use
Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy") to reformat the date from, say, 25.05.2015 (that is, the value in the cell) to Mon 25.05.2015. However, what I get instead is Sun 24.05.2015.
Does anybody know what is going on?
Thank you in advance.
Nicola
Check the time zone setting in the script editor. Under the FILE menu, choose PROJECT PROPERTIES in the script editor. It's possible to have a different time zone setting in Apps Script, than is in the spreadsheet. This is a common issue that arises. Apps Script allows a separate time zone setting from the spreadsheet. Also, even if the time is only off by one minute, if the time setting of the date is all zeros, it's common to get the problem that you are having. When a user enters a date, it's possible that no time setting is made. So the time is set to all zeros. The date is correct, but the time is all zeros. Even if the date was typed in at 3 in the afternoon, for example, and the date is correct, the time setting can be midnight of that day. So, even if you subtracted one second from that date, it would now be the day before.

How to copy paste a google spreadsheet table into gmail using google script?

I have a google spreadsheet with a pivot table, which i want to be emailed automatically based on certain criteria using my gmail. I am quite happy with the email script and the cinditions however I cannot find anywhere a script that can effectively replicate a "copy - paste" of the table into the email.
I would appreciate any help you could offer with this.
Thanks
Agi
I managed to get it to work using advice from this community. Thank you!
I am trying to get it to display values horizontally i.e. each line a new customer record. However, i get something different. I am attaching a file with what I get and the code. I would appreciate any advice. I am so sure that the solution is just there but after many hours I cannot get it :( I am showing the code below and I am attaching a photo with the current and desired outcome.
Many thanks
Agi
function testMail(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var responses = ss.getSheetByName("Monitoreo de Pedidos");
// var lastRow = responses.getLastRow();
// var values = responses.getRange("A2"+(lastRow)+":R2"+(lastRow)).getValues();
var values = responses.getRange("A3:R12").getValues();
var headers = responses.getRange("A2:R2").getValues(); //The headers. A1 TO R1 does not contain relevant data
var message = composeMessage (headers,values);
var messageHTML = composeHtmlMsg(headers,values);
Logger.log(messageHTML);
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'test html', message,{'htmlBody':messageHTML});
}
function composeMessage(headers,values){
var message = 'Here are the data you submitted :\n'
for (var j=0;j<5;++j){ //NUMBER OF ROWS
for(var c=0;c<values[0].length;++c){ //NUMBER OF COLUMNS, in the future this will be dynamic
message+='\n'+headers[0][c]+' : '+values[j][c]
}
}
return message;
}
function composeHtmlMsg(headers,values){
var message = 'Here are the data you submitted :<br><br><table style="background- color:yellow;border-collapse:collapse;" border = 1 cellpadding = 5><th>Title</th> <th>Customer Info</th><tr>'
for (var j=0;j<5;++j){
for(var c=0;c<values[0].length;++c){
message+='<tr><td>'+headers[0][c]+'</td><td>'+values[j][c]+'</td></tr>'
}
}
return message+'</table>';
![Desired outcome][1]}
You need to write the email body as html and insert the in there. More work if you want to mimic formatting. Try if and write back if you get stuck.