Change cell Data on Spreadsheet - javascript

I am trying to take a template spreadsheet, and copy it, then change the data and export as pdf, finally email it.
So far I can do everything except change it on the sheet. It even changes in the logger. Can someone help with this?
Code as follows:
function After_Submit(e, values){
var range = e.range;
var row = range.getRow(); //get the row of newly added form data
var sheet = range.getSheet(); //get the Sheet
var headers = sheet.getRange(1, 1, 1,5).getValues().flat(); //get the header names from A-O
var data = sheet.getRange(row, 1, 1, headers.length).getValues(); //get the values of newly added form data + formulated values
var values = {}; // create an object
for( var i = 0; i < headers.length; i++ ){
values[headers[i]] = data[0][i]; //add elements to values object and use headers as key
}
const pdfFile = Create_PDF(values);
sendEmail(e.namedValues['Your Email'][0],pdfFile);
}
function sendEmail(email,pdfFile,){
GmailApp.sendEmail(email, "Subject", "Message", {
attachments: [pdfFile],
name: "From Someone"
});
}
function Create_PDF(values, ) {
const PDF_folder = DriveApp.getFolderById("1t_BYHO8CqmKxVIucap_LlE0MhslpT7BO");
const TEMP_FOLDER = DriveApp.getFolderById("1TNeI1HaSwsloOI4KnIfybbWR4u753vVd");
const PDF_Template = DriveApp.getFileById('1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8');
const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER);
const openDoc = SpreadsheetApp.openById(newTempFile.getId());
const newOpenDoc = openDoc.getSheetByName("Sheet1");
var body = newOpenDoc.getDataRange().getValues();
for (const key in values) {
body = body.toString().replace("{{"+key+"}}", values[key]);
}
Logger.log(body);
newOpenDoc.getDataRange().setValues(body);
SpreadsheetApp.flush();
const BLOBPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = PDF_folder.createFile(BLOBPDF);
console.log("The file has been created ");
return pdfFile;
}

When I saw your script, in the for loop of the function Create_PDF, an array retrieved from the sheet is converted to a string. I thought that this might be the reason for your issue.
When this issue was removed, how about the following modification?
From:
var body = newOpenDoc.getDataRange().getValues();
for (const key in values) {
body = body.toString().replace("{{"+key+"}}", values[key]);
}
To:
values = Object.fromEntries(Object.entries(values).map(([k, v]) => [`{{${k}}}`, v]));
var body = newOpenDoc.getDataRange().getValues().map(r => r.map(c => values[c] || c));
By this modification, the converted value by values are put into the sheet.
Reference:
map()

Related

Get files URLs from folders ID [duplicate]

I would like to get the files URL and Name from an array of folders ID, which I'm retrieing with a script.
I would like the result to be placed on the Files tab.
The script I'm using for getting the filesID is the following:
function Folders_URL() {
var ss=SpreadsheetApp.getActive().getSheetByName("Overview");
var fldr=DriveApp.getFolderById("someID");
var files=fldr.getFolders();
var names=[],f,str; while (files.hasNext()) {
f=files.next();
names.push([f.getId(), f.getName()]);
}
ss.getRange(4,2,names.length, 2).setValues(names);
}
For getting files Names with the URL, I use this code.
It would be very helpful if someone can join both codes or just getting the URLs and File names from the array of foldersID
function getFileNames() {
var sheet = SpreadsheetApp.getActive().getSheetByName("someLink");
//Range where the links are set
var links = sheet.getRange("F3:F").getValues();
var filenames = [];
for (var i = 0; i < links.length; i++) {
var url = links[i][0];
if (url != "") {
var filename = SpreadsheetApp.openByUrl(links[i][0]).getName();
filenames.push([filename]);
}
}
//Define cell range
var startRow = 3; // print in row 2 since row 1 is the header row
var fileNameColumn = 4; // Column B = column 2
var destinationCell = sheet.getRange(startRow, fileNameColumn, filenames.length, filenames[0].length);
destinationCell.setValues(filenames);
}
Please be aware that I cannot share any sheet since I have sensitive information from my end.
Thanks!
UPDATED ANSWER
New error when running the script:
Get Urls
I tested this code on my data and it works fine. If it's not working on your data then the problem is your data.
function getUrls() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const osh = ss.getSheetByName("Sheet1");
const vs = sh.getRange(4, 1, sh.getLastRow() - 3, sh.getLastColumn()).getValues();
let obj = { pA: [] };
vs.forEach((r, i) => {
let fldr = DriveApp.getFolderById(r[1]);
let files = fldr.getFilesByName(r[2]);
while (files.hasNext()) {
let file = files.next();
let name = file.getName();;
if (!obj.hasOwnProperty(name)) {
obj[name] = [file.getUrl()];
obj.pA.push(name)
} else {
obj[name].push(file.getUrl())
}
}
});
if (obj.pA.length > 0) {
let o = obj.pA.map(p => [p, obj[p].join('\n')]);
osh.clearContents();
if(o.length > 0) {
osh.getRange(1, 1, o.length, o[0].length).setValues(o);
}
}
}

Google Apps Script add paragraph to Google Document in specific location based on date

I'm using Google Forms to create an easy method of adding stories and photos to a Google doc for a collective history/journal.
My code takes the Google form responses from the linked Google sheet and then just appends the Google Form responses to the Google doc but I would like to add the responses sorted by the date that gets submitted in the Google form. That way an event that gets submitted that took place on 01/01/2020 will be listed before an event that took place on 01/02/2020 etc.
How would I go about doing that?
function autoFillGoogleDocFromForm(e) {
var timestamp = e.values[0];
var photo = e.values[1];
var date = e.values[2];
var event = e.values[3];
var name = e.values[4];
var photoCap = e.values[6];
var photoDesc = e.values[7];
var fileURL = photo;
var fileID = fileURL.substr(fileURL.search("=")+1); //strip off text before id= in the URL
var image = DriveApp.getFileById(fileID).getBlob();
var doc = DocumentApp.openById("1DrE4ElgaP08uOTH52E2GjgmrJmoL2VZsZ1YlNeV0_20")
var body = doc.getBody();
body.appendPageBreak();
body.appendParagraph(date);
body.appendParagraph(event);
body.appendParagraph(name);
body.appendImage(image);
body.appendParagraph(photoCap);
body.appendParagraph(photoDesc);
doc.saveAndClose();
}
Here is an example of how to insert paragraphs based on date. I use Date object to compare dates so I convert a text string in the form "1/1/2022" to a Date object.
The format of your Doc must have the date string directly following the Page Break.
function testAutoFillGoogleDocFromForm() {
try {
let row = { values: [ "time", "photo", "2/1/2022", "event", "name", "", "photoCap", "photoDesc" ]};
autoFillGoogleDocFromForm(row);
console.log("done");
row = { values: [ "time", "photo", "1/1/2023", "event", "name", "", "photoCap", "photoDesc" ]};
autoFillGoogleDocFromForm(row);
console.log("done");
}
catch(err) {
console.log(err)
}
}
function autoFillGoogleDocFromForm(e) {
try {
let timestamp = e.values[0];
let photo = e.values[1];
let date = new Date(e.values[2]);
let event = e.values[3];
let name = e.values[4];
let photoCap = e.values[6];
let photoDesc = e.values[7];
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let i = 0;
while( i < body.getNumChildren() ) {
let para = body.getChild(i);
if( para.getType() === DocumentApp.ElementType.PARAGRAPH ) {
let j = 0;
console.log("numchild = "+body.getNumChildren());
while( j < para.getNumChildren() ) {
let child = para.getChild(j);
if( child.getType() === DocumentApp.ElementType.PAGE_BREAK ) {
// get next paragraph and check date
if( (i+1) >= body.getNumChildren() ) break; // in case there is a page break at the end of body
para = body.getChild(i+1);
let temp = new Date(para.asParagraph().getText());
console.log(temp);
if( temp > date ) {
body.insertPageBreak(i++);
body.insertParagraph(i++,date.toLocaleDateString());
body.insertParagraph(i++,event);
body.insertParagraph(i++,name);
body.insertParagraph(i++,photoCap);
body.insertParagraph(i++,photoDesc);
return;
}
}
j++;
}
}
i++;
}
// if the date is latest just append a new page
body.appendPageBreak();
body.appendParagraph(date.toLocaleDateString());
body.appendParagraph(event);
body.appendParagraph(name);
//body.appendImage(image);
body.appendParagraph(photoCap);
body.appendParagraph(photoDesc);
}
catch(err) {
console.log(err)
}
}

Copy values to next column(if blank) in the same row. If not blank, copy to the second column(if blank) in same row. Repeat

Disclaimer: Im very new to google scripts. I jumbled together this code with mixed success.
When I run the script, it works fine with the first two attempts. Then it doesnt work after that because column Q now has values in other cells within the column and the script is technically correct but not running at intended. I need to ignore column Q cells that are not blank and still run the script to copy P values to the other cells in column Q.
Also, when column Q with in the same row is not blank, I need to copy the value from column P to column R (if blank). Rinse and Repeat script...
function copyVals () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange ("Sheet1!P2:P");
var destSheet = ss.getSheetByName("Sheet1");
var destRange = destSheet.getRange('Q2:Q')
var destRange2 = destSheet.getRange('R2:R')
if (destRange.isBlank()) {
source.copyTo (destRange, {contentsOnly: true});
source.clear ();
}
if (destRange2.isBlank()) {
source.copyTo (destRange2, {contentsOnly: true});
source.clear ();
}
}
You need to do the blank check for each cell separately
However, if you do it wiht the Apps Script method isBlank() - this will make your code a bit slow.
I suggest you to
retreive the existing values both in the source and destination ranges with getValues
check for each of the destinations values either those are empty and replace the empty values through source values
assign the modified values back to the sheet with setValues
Sample code:
function copyVals () {
var ss = SpreadsheetApp.getActiveSpreadsheet ()
var sheet = ss.getSheetByName("Sheet1")
var lastRow = sheet.getLastRow()
var source = sheet.getRange ("P2:P" + lastRow)
var destSheet = sheet
var destRange = destSheet.getRange('Q2:Q' + lastRow)
var destRange2 = destSheet.getRange('R2:R' + lastRow)
var sourceValues = source.getValues().flat()
var destValues = destRange.getValues()
var dest2Values = destRange2.getValues()
sourceValues.forEach(function(value, i){
console.log("i" + i)
if (destValues[i][0] == "") {
destValues[i][0] = value
}
if (dest2Values[i][0] == "") {
dest2Values[i][0] = value
}
})
destRange.setValues(destValues)
destRange2.setValues(dest2Values)
source.clear ();
}
UPDATE
If you want to copy to column Q and R alternately, you can use script properties to save the run count of the script and execute different code blocks for odd and even number.
Sample:
function copyVals () {
var ss = SpreadsheetApp.getActiveSpreadsheet ()
var sheet = ss.getSheetByName("Sheet1")
var lastRow = sheet.getLastRow()
var source = sheet.getRange ("P2:P" + lastRow)
var destSheet = sheet
var destRange = destSheet.getRange('Q2:Q' + lastRow)
var destRange2 = destSheet.getRange('R2:R' + lastRow)
var sourceValues = source.getValues().flat()
var destValues = destRange.getValues()
var dest2Values = destRange2.getValues()
var scriptProperties = PropertiesService.getScriptProperties()
var myProperty = scriptProperties.getProperty('timesCalled')
if (!myProperty){
myProperty = "1"
}
myProperty = JSON.parse(myProperty)
var isOdd = myProperty % 2
if(isOdd){
sourceValues.forEach(function(value, i){
console.log("i" + i)
if (destValues[i][0] == "") {
destValues[i][0] = value
}
})
destRange.setValues(destValues)
} else{
sourceValues.forEach(function(value, i){
if (dest2Values[i][0] == "") {
dest2Values[i][0] = value
}
})
destRange2.setValues(dest2Values)
}
source.clear ()
myProperty++
scriptProperties.setProperty('timesCalled', JSON.stringify(myProperty))
}

Reading excel file into array using javascript

I'm trying to read an excel file and create a multidimensional array in javascript with it.
The excel file will look like:
AA11 AA22 AN65
AB11 AB22 AN64
...
I need it to create an array that looks like:
[
[AA11, AA22, AN65],
[AB11, AB22, AN64]
]
So far, I've been able to bring up a file selection window, and I believe it's reading the file, I just think it might not be putting the data into the array correctly. This is what I have so far:
<script type="text/javascript">
$(function () {
$("#input").on("change", function () {
var excelFile,
var array = [[],[]];
fileReader = new FileReader();
$("#result").hide();
fileReader.onload = function (e) {
var buffer = new Uint8Array(fileReader.result);
$.ig.excel.Workbook.load(buffer, function (workbook) {
var column, row, newRow, cellValue, columnIndex, i,
worksheet = workbook.worksheets(0),
columnsNumber = 0,
gridColumns = [],
data = [],
worksheetRowsCount;
while (worksheet.rows(0).getCellValue(columnsNumber)) {
columnsNumber++;
}
for (columnIndex = 0; columnIndex < columnsNumber; columnIndex++) {
column = worksheet.rows(0).getCellText(columnIndex);
gridColumns.push({ headerText: column, key: column });
}
for (i = 1, worksheetRowsCount = worksheet.rows().count() ; i < worksheetRowsCount; i++) {
newRow = {};
row = worksheet.rows(i);
for (columnIndex = 0; columnIndex < columnsNumber; columnIndex++) {
cellValue = row.getCellText(columnIndex);
//newRow[gridColumns[columnIndex].key] = cellValue;
array[row,columnIndex] = cellValue;
}
window.alert(array[0][0]);
data.push(array);
}
</script>
Any help would be greatly appreciated.
Not sure what you're using to parse the Excel, is it IgniteUI ? For what it's worth, the free (community edition) of SheetJS, js-xlsx provides a few functions that produce exactly the output you needed, given the spreadsheet you provided.
The docs are a bit messy, but they are complete, the most interesting sections for this use-case are: Browser file upload form element under Parsing workbooks and XLSX.utils.sheet_to_json. You can run a test with the type of spreadsheet you provided in the code sample below:
$("#input").on("change", function (e) {
var file = e.target.files[0];
// input canceled, return
if (!file) return;
var FR = new FileReader();
FR.onload = function(e) {
var data = new Uint8Array(e.target.result);
var workbook = XLSX.read(data, {type: 'array'});
var firstSheet = workbook.Sheets[workbook.SheetNames[0]];
// header: 1 instructs xlsx to create an 'array of arrays'
var result = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
// data preview
var output = document.getElementById('result');
output.innerHTML = JSON.stringify(result, null, 2);
};
FR.readAsArrayBuffer(file);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="input" accept=".xls,.xlsx,.ods">
<pre id="result"></pre>
Here the full solution aditionally I've added a group by category function
to demostrante that we can apply functions to the json array.
(async() => {
const url = "./yourfile.xlsx";
const data = await (await fetch(url)).arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const sheetValues = XLSX.utils.sheet_to_json(worksheet);
const groupByCategory = sheetValues.reduce((group, product) => {
const { category } = product;
group[category] = group[category] ?? [];
group[category].push(product);
return group;
}, {});
console.log(groupByCategory)
/* DO SOMETHING WITH workbook HERE */
})();

Compare value to another spreadsheet using array loop and write new values

Hello all I'm having trouble implementing array loops in my project... Here is what I want to do.
I have a spreadsheet called "Red Book" this sheet gets updated regularly once the staff have updated it I have a column where they can select to submit the data they've just entered on that specific row (editing this column calls an onEdit function).
The data will then be written to another spreadsheet (different file) called "Raw Data"
For each submit I have a unique identifier. I need the onEdit code to do the following...
Iterate through the column A to find the unique identifier
Once found update the data in columns 1 through 5
Below is the script I have so far:
function TransferToAppData(e) {
var destFile = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
var destSheet = destFile.getSheetByName("Raw App Data");
var ss = e.source;
var s = ss.getActiveSheet();
var uniConstRng = s.getRange("A1");
var uniqueConstVal = uniConstRng.getValue();
var NextOpenRow = destSheet.getLastRow() + 1;
var ActiveRow = e.range.getRow();
Logger.log(ActiveRow);
var uniqueVal = s.getRange(ActiveRow,1).getValue();
var add = s.getRange(ActiveRow,2).getValue();
var name = s.getRange(ActiveRow,3).getValue();
var dt = s.getRange(ActiveRow,5).getValue()
if (uniqueVal == "") {
s.getRange(ActiveRow,1).setValue(uniqueVal + 1);
uniConstRng.setValue(uniqueVal + 1);
var transferVals = s.getRange(ActiveRow,1,1,5).getValues();
Logger.log(transferVals);
destSheet.getRange(NextOpenRow,1,1,5).setValues(transferVals);
destSheet.getRange(NextOpenRow, 6).setValue("Applicant");
}
else {
var destLastRow = destSheet.getLastRow();
var destDataRng = destSheet.getRange(2,1,destLastRow,5)
var destValues = destDataRng.getValues();
var sourceValues = s.getRange(ActiveRow,1,1,5).getValues();
for( var i = 0; i < destValues.length; ++i){
if (destValues([i][0])==uniqueVal) {
for(n=0;n<destValues[0].length;++n){
///I"m stuck!!!
}
}
}
}
}
As you can see I have the first array loop going, but I'm having trouble figuring out how to do a second loop that iterates only on the row where the unique value is found and write the source data to ONLY to row where the unique value was found not the whole sheet.
I figured it out...
Below is the code and here is how it works...
When values in certain columns are edited this code is fired.
1--It finds the unique identifier located in the row which was edited.
2--Compares that identifier with a column of unique identifiers in another spreadsheet.
3--When a match is found it writes the change to the new spreadsheet and exits the loop
function TransferToAppData(e) {
var destFile = SpreadsheetApp.openById('1V3R2RnpA8yXmz_JDZSkBsK9tGR2LjHZp52p5I1CuQvw');
var destSheet = destFile.getSheetByName("Raw App Data");
var ss = e.source;
var s = ss.getActiveSheet();
var uniqueConstRng = s.getRange("A1");
var uniqueConstVal = uniqueConstRng.getValue();
var NextOpenRow = destSheet.getLastRow() + 1;
var ActiveRow = e.range.getRow();
var uniqueVal = s.getRange(ActiveRow,1).getValue();
if (s.getRange(ActiveRow,2).getValue() == "" || s.getRange(ActiveRow,3).getValue()=="" || s.getRange(ActiveRow,4).getValue()=="" || s.getRange(ActiveRow,5).getValue()=="") {
s.getRange(ActiveRow,13).clearContent();
Browser.msgBox("Address, Name, Date Entered & Rent are required fields!");
} else{
if (uniqueVal == "") {
s.getRange(ActiveRow,1).setValue(uniqueConstVal + 1);
uniqueConstRng.setValue(uniqueConstVal + 1);
var transferVals = s.getSheetValues(ActiveRow,1,1,5);
destSheet.getRange(NextOpenRow,1,1,5).setValues(transferVals);
destSheet.getRange(NextOpenRow, 6).setValue("Applicant");
}
else {
var destLastRow = destSheet.getLastRow();
var destValues = destSheet.getSheetValues(2,1,destLastRow,5);
var sourceValues = s.getSheetValues(ActiveRow,1,1,5);
for(var i = 0; i < destValues.length; ++i){
if (destValues[i][0]===uniqueVal) {
destSheet.getRange(i+2,1,1,5).setValues(sourceValues);
break;
}
}
}
s.sort(1,false);
destSheet.sort(1,false);
}
}

Categories

Resources