I am trying to figure out how to make a script to run a function based on an answer provided, at the moment my script creates and populates a word doc with answers provided from a google form. I want to streamline it so that I can use one google form to create different documents based on an answer provided rather than creating multiple google forms. I am very new to JavaScript and I am pretty sure I need an if statement at the beginning of everything, but I don't know what I should write or where it should go. This is my script:
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs(){
const googleDocTemplate = DriveApp.getFileById('google file goes here');
const destinationFolder = DriveApp.getFolderById('google folder goes here');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Response');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row,index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[5]} document name goes here`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
body.replaceText('{{Name}}', row[5]);
body.replaceText('{{To}}', row[6]);
body.replaceText('{{Items}}', row[7]);
body.replaceText('{{Reasoning}}', row[8])
body.replaceText('{{Submitter}}', row[1]);
body.replaceText('{{Role}}', row[2]);
body.replaceText('{{Time}}', row[3]);
body.replaceText('{{Discord}}', row[4]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}
Related
Have found nothing online on this:
I have built a script that repeatedly populates a sheet before copying it to a separate spreadsheet. The issue is my script behaves differently if executed from the editor versus when triggered from inside the spreadsheet.
When run from the Apps Script editor, the script runs as expected whereas when run from a custom menu item or a trigger in an embedded photo, my function populating my original sheet doesn't have a chance to finish execution before it is copied to a separate spreadsheet.
When executed from a trigger, my separate spreadsheet's copied sheet resembles my original sheet mid-execution. It gets copied before the function populating the original sheet is done executing. This is a problem since my code is meant for another person to use it directly from the spreadsheet.
edit (here's the code):
//loop thru supervisor, create new page
for (let i = 0; i < swConcise.length; i++){
let swName = swConcise[i];
//draw social worker report function
batchSocialWorker(swName);
//copy to alt ss
const sheet = baseSS.getSheetByName('SocialWorkersReport');
sheet.copyTo(newSS);
//rename sheet
newSS.getSheets()[i+1].setName(swName);
}
Edit (longer code if you need):
function batchSocialWorkers(){
/*
1. create new ss, set baseSS, newSS vars
2. create directors array
3. loop directors, copy sheet to newSS and rename
4. create link in new sheet
*/
const baseSS = SpreadsheetApp.getActiveSheet();
//create new ss titled date, sw Report
const today = date();
const title = "Social Workers Reports " + today;
const id = createSS (title);
const newSS = SpreadsheetApp.openById(id);
//scrape social workers names
const swAll = baseSS.getSheetByName('Relationships').getRange('F11:F').getValues();
//create concise s0cial workers array
const swConcise = cleanupArray(swAll);
//loop thru supervisor, create new page
for (let i = 0; i < swConcise.length; i++){
let swName = swConcise[i];
//draw social worker report function
batchSocialWorker(swName);
//copy to alt ss
const sheet = baseSS.getSheetByName('SocialWorkersReport');
sheet.copyTo(newSS);
//rename sheet
newSS.getSheets()[i+1].setName(swName);
}
//delete first 2 sheets
newSS.deleteSheet(newSS.getSheets()[0]);
newSS.deleteSheet(newSS.getSheets()[0]);
//provide link to new sheet
const link = 'https://docs.google.com/spreadsheets/d/' + id;
var ui = SpreadsheetApp.getUi();
ui.alert('Batch Social Workers Reports Created', link, ui.ButtonSet.OK);
}
function createSS (title) {
// This code uses the Sheets Advanced Service, but for most use cases
// the built-in method SpreadsheetApp.create() is more appropriate.
try {
let sheet = Sheets.newSpreadsheet();
sheet.properties = Sheets.newSpreadsheetProperties();
sheet.properties.title = title;
const spreadsheet = Sheets.Spreadsheets.create(sheet);
return spreadsheet.spreadsheetId;
} catch (err) {
// TODO (developer) - Handle exception
console.log('Failed with error %s', err.message);
}
}
function cleanupArray (array){
let newArray = new Array();
for (let i = 0; i < array.length; i++){
if (array[i][0] != '') newArray.push(array[i][0]);
}
return newArray;
}
This is partly a guess because you have not provided all of the code:
But try this. I still don't know which functions you are calling by a trigger. But I did see several errors in your code. I tried to repair what I can.
function batchSocialWorkers() {
const bss = SpreadsheetApp.getActive();
const title = "Social Workers Reports " + Utilities.formatDate(new Date(),bss.getSpreadsheetTimeZone(),"MM/dd/yyyy");//guess at the format
const ss = SpreadsheetApp.create(title);
const sh = bss.getSheetByName('Relationships')
const vs = sh.getRange('F11:F' + sh.getLastRow()).getValues();
for (let i = 0; i < vs.length; i++) {
let swName = vs[i];
batchSocialWorker(swName);
const sheet = bss.getSheetByName('SocialWorkersReport');
sheet.copyTo(ss);
ss.getSheets()[i + 1].setName(swName);
}
ss.deleteSheet(ss.getSheets()[0]);
ss.deleteSheet(ss.getSheets()[0]);
const link = 'https://docs.google.com/spreadsheets/d/' + id;
var ui = SpreadsheetApp.getUi();
ui.alert('Batch Social Workers Reports Created', link, ui.ButtonSet.OK);
}
I need to be able to take all files from a a folder within drive and input the data into the spreadsheet. I am also creating a Menu Tab so I can just Run the script without going to editor. It would be great if I can create a way enter names of existing folder name without always going to the script in order to take out that extra step. This is the script I am using. I really need assistance with this.
function importTimesheets() {
var spreadsheets = DriveApp.
getFolderById("").
getFilesByType(MimeType.GOOGLE_SHEETS);
var data = [];
while (spreadsheets.hasNext()) {
var currentSpreadsheet = SpreadsheetApp.openById(spreadsheets.next().getId());
data = data.concat(currentSpreadsheet
.getSheetByName('Timesheet')
.getRange("A3:L10")
.getValues()
);
}
SpreadsheetApp.
getActiveSheet().
getRange(1, 1, data.length, data[0].length).
setValues(data);
}
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Generate Timesheets')
.addItem('Generate', 'importTimesheets')
As far as I understand you are trying to find a solution for this line of code, whereby you currently have to enter the Id manually.
DriveApp.getFolderById("")
My suggestions would be to prompt the user for the folder Id, because prompting for the folder name may cause errors if more than one folder has the same name. My suggestion is implemented as follows:
const folderId = SpreadsheetApp.getUi().prompt("Please enter the Folder Id").getResponseText()
const folder = DriveApp.getFolderById(folderId)
You could also use the Google File/Folder picker, as described here to search and select the folder.
Try this:
var level=0;
function getFnF(folder = DriveApp.getRootFolder()) {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1')
const files=folder.getFilesByType(MimeType.GOOGLE_SHEETS);
while(files.hasNext()) {
let file=files.next();
let firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('File: %s', file.getName()));//need editing
}
const subfolders=folder.getFolders()
while(subfolders.hasNext()) {
let subfolder=subfolders.next();
let forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('Fldr: %s', subfolder.getName()));//needs editing
level++;
getFnF(subfolder);
}
level--;
}
function runThisFirst() {
let r = SpreadsheetApp.getUi().prompt('Folder id','Enter Folder Id',SpreadsheetApp.getUi().ButtonSet.OK);
let folder = DriveApp.getFolderById(r.getResponseText())
getFnF(folder);
}
I am trying to create an app that, with info given by the user, gets a PDF formulary from local assets, writes that info on it and then downloads it.
It works perfectly fine on browser and I get the file sucessfully, but when I create the app build and I try it in the movile device, just nothing happens. I get messages of "File downloaded!" or any log I put, but the file just never start downloading. I already have storage permissions. Just ends the function normally but ignoring the download.
I use the unpkg library to download, but also tried with SaveAs and creating an <a.href> then click it but I got excactly the same result.
This is the full function:
async generateDecklist()
{
const formUrl=('../../../assets/decklist.pdf');
const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(formPdfBytes);
const form = pdfDoc.getForm();
const formFields = form.getFields()
//START FILLING FORM
//Deck Name & Nation
form.getTextField('Deck NameRow1').setText(this.deck.name);
form.getTextField('ClanRow1').setText(this.deck.nation);
for (let i = 0; i<this.deck.decklist.length; i++)
{
const cs = this.deck.decklist[i];
const card = Global.cards.find(e => e.id == cs.cardId);
//Card Name
const mainDeckCard = "Main deck 50 cardsRow" + (i+1);
form.getTextField(mainDeckCard).setText(card.name);
//Card Grade
formFields[16+i].setText(card.grade.toString());
//Card Amount
const mainDeckAmount = "Main deck Qty" + (i+1);
form.getTextField(mainDeckAmount).setText(cs.amount.toString());
//Card set (Only if there is only one set)
if(card.sets.length ==1)
{
const mainDeckSet = 'No.'+ ((i+13)>=15? (i+14) : (i+13));
form.getTextField(mainDeckSet).setText(card.sets[0]);
}
//Card trigger or sentinel
let triggerOrSentinel ='';
if(card.type == 'Trigger Unit')
{
triggerOrSentinel += card.trigger;
}
if(card.keywords.includes('Sentinel'))
{
triggerOrSentinel += triggerOrSentinel==''? 'Sentinel' : '/Sentinel';
}
const mainDecktrigger = "Main deck Row" + (i+1);
form.getTextField(mainDecktrigger).setText(triggerOrSentinel);
}
//END FILLING FORM
//PROBLEM STARTS HERE
const pdfBytes = await pdfDoc.save();
download(pdfBytes, `${this.deck.name}_decklist.pdf`, "application/pdf");
modalController.dismiss();
}
I need to insert some line breaks before certain text in a Google Document.
Tried this approach but get errors:
var body = DocumentApp.getActiveDocument().getBody();
var pattern = "WORD 1";
var found = body.findText(pattern);
var parent = found.getElement().getParent();
var index = body.getChildIndex(parent);
// or parent.getChildIndex(parent);
body.insertParagraph(index, "");
Any idea on how to do this?
Appreciate the help!
For example, as a simple modification, how about modifying the script of https://stackoverflow.com/a/65745933 in your previous question?
In this case, InsertTextRequest is used instead of InsertPageBreakRequest.
Modified script:
Please copy and paste the following script to the script editor of Google Document, and please set searchPattern. And, please enable Google Docs API at Advanced Google services.
function myFunction() {
const searchText = "WORD 1"; // Please set text. This script inserts the pagebreak before this text.
// 1. Retrieve all contents from Google Document using the method of "documents.get" in Docs API.
const docId = DocumentApp.getActiveDocument().getId();
const res = Docs.Documents.get(docId);
// 2. Create the request body for using the method of "documents.batchUpdate" in Docs API.
let offset = 0;
const requests = res.body.content.reduce((ar, e) => {
if (e.paragraph) {
e.paragraph.elements.forEach(f => {
if (f.textRun) {
const re = new RegExp(searchText, "g");
let p = null;
while (p = re.exec(f.textRun.content)) {
ar.push({insertText: {location: {index: p.index + offset},text: "\n"}});
}
}
})
}
offset = e.endIndex;
return ar;
}, []).reverse();
// 3. Request the request body to the method of "documents.batchUpdate" in Docs API.
Docs.Documents.batchUpdate({requests: requests}, docId);
}
Result:
When above script is used, the following result is obtained.
From:
To:
Note:
When you don't want to directly use Advanced Google services like your previous question, please modify the 2nd script of https://stackoverflow.com/a/65745933 is as follows.
From
ar.push({insertPageBreak: {location: {index: p.index + offset}}});
To
ar.push({insertText: {location: {index: p.index + offset},text: "\n"}});
References:
Method: documents.get
Method: documents.batchUpdate
InsertTextRequest
I'm trying to fetch the data from the sheet cell B1 and do the math with that data but it doesn't work when I this line
var source = SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue();
But when i remove the above line, it works all fine.can i know the reasone? here is my code,
document.getElementById("btn").addEventListener("click", doStuff);
function doStuff() {
var source = SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue(); //Separate spreadsheet book
const rate = 0.155;
const vprice = document.getElementById("price").value;
const downPayment = document.getElementById("DownPay").value;
const borrow = vprice-downPayment;
const period = 12;// add motnhs here
const opp = vprice*0.5;
var nf = new Intl.NumberFormat(); //number format
const subL = Math.round((vprice*source)/2);
Unfortunately, in the current stage, at HTML&Javascript side (client side), Google Apps Script cannot be directly used. In your case, I think that google.script.run can be used for retrieving the values from Google Apps Script. So please modify as follows.
HTML&Javascript side:
Please modify doStuff() as follows.
function doStuff() {
google.script.run.withSuccessHandler(source => {
const rate = 0.155;
const vprice = document.getElementById("price").value;
const downPayment = document.getElementById("DownPay").value;
const borrow = vprice-downPayment;
const period = 12;// add motnhs here
const opp = vprice*0.5;
var nf = new Intl.NumberFormat(); //number format
const subL = Math.round((vprice*source)/2);
}).getValues();
}
Google Apps Script side:
Please add the following function.
function getValues() {
return SpreadsheetApp.openById('1mkbnIlDt6WGltQg6peKojBFPz0JwyjAkJGwTYbLYEmk').getSheetByName('HardEdit').getRange('B1').getValue();
}
In this modification, when doStuff() is run, getValues() at Google Apps Script is run by google.script.run. And the returned value from getValues() can be retrieved at Javascript side using withSuccessHandler.
References:
Class google.script.run