Saving a Google Sheet as epub - google-apps-script

I have a Google Sheet with content in say Col 1. One sentence in each row. I am looking for a script which can save the Col 1 as 'epub' with each sentence (in row) as a new page.

I believe your current situation and your goal as follows.
In your Spreadsheet, there are the sentences in each row of the column "A".
You want to retrieve a value from a cell of column "A" and convert it as a file of EPUB on your Google Drive.
You want to achieve this using Google Apps Script.
In this case, I would like to propose the following flow.
Retrieve the values from the column "A" of the Spreadsheet.
Create Google Document as the temporal file.
Copy the values to Google Document.
Export Google Document as EPUB of application/epub+zip and save it as a file on Google Drive.
Remove the temporal file.
When above flow is reflected to the script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor of Google Spreadsheet you want to use. And, please run myFunction. By this, the values are retrieved from the cells "A1:A" and create EPUB files using the values of each row.
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
const folderId = "root"; // Please set the folder ID you want to export the EPUB files. In the case of "root", the files are created to the root folder.
// 1. Retrieve the values from the column "A" of the Spreadsheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const startRow = 1;
const endRow = sheet.getLastRow();
const values = sheet.getRange(`A${startRow}:A${endRow}`).getDisplayValues();
// 2. Create Google Document as the temporal file.
const tempDoc = DocumentApp.create("temp");
const id = tempDoc.getId();
const url = "https://docs.google.com/feeds/download/documents/export/Export?exportFormat=epub&id=" + id;
const params = {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}};
const folder = DriveApp.getFolderById(folderId || "root");
const ids = values.map(([a], i) => {
// 3. Copy the values to Google Document.
const filename = `rowNumber${i + 1}`;
const doc = DocumentApp.openById(id).setName(filename);
doc.getBody().clear().appendParagraph(a);
doc.saveAndClose();
// 4. Export Google Document as EPUB of `application/epub+zip` and save it as a file on Google Drive.
const blob = UrlFetchApp.fetch(url, params).getBlob().setName(`${filename}.epub`);
return folder.createFile(blob).getId();
});
console.log(ids); // Here, you can see the file IDs of the created EPUB files at the log.
// 5. Remove the temporal file.
DriveApp.getFileById(id).setTrashed(true);
}
In this sample script, the filename is rowNumber${i + 1}. So, the created filename is like rowNumber1.epub, rowNumber2.epub. If you want to change this, please modify above script.
The endpoint of const url = "https://docs.google.com/feeds/download/documents/export/Export?exportFormat=epub&id=" + id; is from exportLinks of the method of "Files: get" of Drive API. Ref
Note:
In this case, when a lot of rows are existing in your Spreadsheet, the process time might be over the maximum execution time of 6 minutes. Please be careful this. If the process time is over the maximum execution time, please modify the values of startRow and endRow.
If an error related to Drive API occurs, please enable Drive API at Advanced Google servicves.
If you want to convert the values of the column "A" as one EPUB file, you can also use the following script.
function myFunction2() {
const sheetName = "Sheet1";
const folderId = "root"; // Please set the folder ID you want to export the EPUB files. In the case of "root", the files are created to the root folder.
const filename = `sampleFile`; // Please set the output filename.
// 1. Retrieve the values from the column "A" of the Spreadsheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const startRow = 1;
const endRow = sheet.getLastRow();
const values = sheet.getRange(`A${startRow}:A${endRow}`).getDisplayValues();
// 2. Create Google Document as the temporal file.
const tempDoc = DocumentApp.create(filename);
// 3. Copy the values to Google Document.
tempDoc.getBody().clear().appendParagraph(values.flat().join("\n"));
tempDoc.saveAndClose();
const id = tempDoc.getId();
// 4. Export Google Document as EPUB of `application/epub+zip` and save it as a file on Google Drive.
const url = "https://docs.google.com/feeds/download/documents/export/Export?exportFormat=epub&id=" + id;
const params = {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}};
const folder = DriveApp.getFolderById(folderId || "root");
const blob = UrlFetchApp.fetch(url, params).getBlob().setName(`${filename}.epub`);
const createdFileId = folder.createFile(blob).getId();
console.log(createdFileId); // Here, you can see the file ID of the created EPUB file at the log.
// 5. Remove the temporal file.
DriveApp.getFileById(id).setTrashed(true);
}
References:
Spreadsheet Service
Class UrlFetchApp
Drive Service

You need to enable Advanced Drive API
function makeEPUB() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');
const rg = sh.getRange(1,1,sh.getLastRow(),1);
const vs = rg.getDisplayValues().flat();//get rows
const document = DocumentApp.create('mydoc');//creat doc
let body = document.getBody();
vs.forEach(s =>{body.appendParagraph(s);body.appendPageBreak();});//append sentences and page breaks
document.saveAndClose();
let exportLink = Drive.Files.get(document.getId()).exportLinks["application/epub+zip"];
let response = UrlFetchApp.fetch(exportLink, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
let file = DriveApp.createFile(response.getBlob());
file.setName(document.getName() + ".epub");
}
Mostly Copied from Amit Agarwal
Don't know if it works. Have no way that I know of to test it.

Related

google drive file link automatically change format in spreadsheet

I am implementing some download and upload file functionality in google drive through google app-script storing the drive link in google sheet. upload works fine but after some time the link is turn into some kind of hyperlink as like below
so that's why I am no longer able to get the link simply writting .getDisplayValue()
const ss = SpreadSheetApp.getActive().getActiveSheet() let url = ss.getRange().getDisplayValue()
Any Suggestion ...?
I tried Adding .getRichTextValue().getLinkUrl() But It also does not worked as It is not a HyperLink
Issue and workaround:
From your sample image, in your situation, the link of the file is changed to the smart chip. In the current stage, unfortunately, there are no methods for managing the smart chips on a Spreadsheet. So, in this case, it is required to use a workaround. The workaround is as follows.
Convert Google Spreadsheet to XLSX data.
By this, the file links of the smart chip are converted to simple strings and hyperlinks.
Convert XLSX data to Google Spreadsheet.
Retrieve the hyperlinks from the cells.
This method is from How to get in Apps Script the value of a dropdown in a Google Doc? and https://tanaikech.github.io/2022/10/27/retrieving-values-of-calendar-events-of-smart-chips-on-google-document-using-google-apps-script/ .
When this flow is reflected in a sample script, how about the following sample script?
Sample script:
Please copy and paste the following script to the script editor of Google Spreadsheet and set range that you want to retrieve the hyperlinks as A1Notation. In this sample, Drive API is used. So, please enable Drive API at Advanced Google services.
function myFunction() {
const range = "Sheet1!A1:A10"; // Please set the range you want to retrieve the hyperlinks.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + ss.getId();
const blob = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob();
const tempId = Drive.Files.insert({ mimeType: MimeType.GOOGLE_SHEETS, title: "temp" }, blob).id;
const tempFile = DriveApp.getFileById(tempId);
const tempSS = SpreadsheetApp.open(tempFile);
const res = tempSS.getRange(range).getRichTextValues().map((r, i) => r.map((c, j) => ({ value: c.getText(), url: c.getLinkUrl() || "", range: { row: i + 1, column: j + 1 } })));
tempFile.setTrashed(true);
console.log(res);
}
Testing:
When this script is run, the following result is obtained.
[
[{"value":"sample value","url":"https://drive.google.com/file/d/###/view?usp=share_link","range":{"row":1,"column":1}}],
,
,
,
]
Note:
As another approach, in your showing sample image, if you want to convert the file links of the smart chip to the normal value with the hyperlink, how about the following sample script? In this sample, range is overwritten by the normal values with the hyperlinks obtained by converting from XLSX data.
function myFunction2() {
const range = "Sheet1!A1:A10"; // Please set the range you want to retrieve the hyperlinks.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + ss.getId();
const blob = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob();
const tempId = Drive.Files.insert({ mimeType: MimeType.GOOGLE_SHEETS, title: "temp" }, blob).id;
const tempFile = DriveApp.getFileById(tempId);
const tempSS = SpreadsheetApp.open(tempFile);
const r = tempSS.getRange(range);
const tempSheet = r.getSheet().copyTo(ss);
tempSheet.getRange(r.getA1Notation()).copyTo(ss.getRange(range));
ss.deleteSheet(tempSheet);
tempFile.setTrashed(true);
}
References:
Retrieving Values of Calendar Events of Smart Chips on Google Document using Google Apps Script (Author: me)
Related thread
How to get in Apps Script the value of a dropdown in a Google Doc?

Name Images Exported From GoogleSheets With Specific ID Pulled From Corrisponding Cell

I have a Google Sheet spreadsheet containing personal data I collect from people who subscribe to my association. They have to complete an online form and sign it. The data is then sent to the spreadsheet and the signature is imported as a PNG in-cell-image.
I need to extract all the PNG signatures and assign them the specific ID found in the same row so I can later match the signature with the correct personal data when generating a PDF form with another script.
ID
Signature
1a2b3c4d
image.png
5e6f7g7h
image.png
I am currently using the following code I found online. It saves all the images to a folder as PNG files but it assigns names like "image-1", "image-2" in a random order.
Here is the code:
function myFunction() {
const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + spreadsheetId;
const blob = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}}).getBlob().setContentType(MimeType.ZIP);
const xlsx = Utilities.unzip(blob);
xlsx.forEach(b => {
const name = b.getName().match(/xl\/media\/(.+)/);
if (name) DriveApp.getFolderById("1mdJbbG_0aF8wjEIuVPsMr9jV31wPINRk").createFile(b.setName(name[1]));
});
}
How can I edit the code to name each file with the corresponding ID?
Thanks a lot!
EDIT:
I collect data from an online form which is displayed in the image below.
Online Form
When clicking on the signature field, a signature pad opens and allows the user to sign.
Signature Pad
Collected data are then sent to the following spreadsheet stored in Google Drive.
Spreadsheet
The script which sends data from the form to the spreadsheet should be the following
function submit(data) {
data = JSON.parse(data)
const headers = SETTINGS.HEADERS.map(({value}) => value)
const id = Utilities.getUuid()
const signatures = []
const values = SETTINGS.HEADERS.map(({key}, index) => {
if (key === "id") return id
if (key === "timestamp") return new Date()
if (!key in data) return null
if (Array.isArray(data[key])) return data[key].join(",")
if (data[key].startsWith("data:image")) {
signatures.push(index)
return SpreadsheetApp.newCellImage().setSourceUrl(data[key]).build().toBuilder()
}
return data[key]
})
const ws = SpreadsheetApp.getActive().getSheetByName(SETTINGS.SHEET_NAME.RESPONSES) || SpreadsheetApp.getActive().insertSheet(SETTINGS.SHEET_NAME.RESPONSES)
ws.getRange(1,1, 1, headers.length).setValues([headers])
const lastRow = ws.getLastRow()
ws.getRange(lastRow + 1, 1, 1, values.length).setValues([values])
signatures.forEach(index => {
ws.getRange(lastRow + 1, index + 1).setValue(values[index])
})
return JSON.stringify({success: true, message: `Grazie per la tua richiesta di iscrizione! ID: ${id}`})
}
The need is to rename the signature image with the submission ID.
In that way, in theory, when I run Tanaike's script to extract the images from the spreadsheet, they should be named with the ID of the corresponding form submission.
As of now, when I run Tanaike's script I get the following output.
Tanaike's script output
Thanks a lot!
I believe your goal is as follows.
You want to export the images of column "O" in Google Spreadsheet. In this case, you want to use the values of column "B" as the filename.
The image is put into the cells as CellImage.
Modification points:
Using XLSX data converted from Spreadsheet, when the image files are directly retrieved from XLSX data, unfortunately, the images cannot correspond to each cell coordinate. I thought that this is the reason of your issue. In this case, it is required to parse the XLSX data. But, I thought that in this case, the script might be a bit complicated. So, in order to retrieve the image data from the XLSX with the cell coordinate, I have created a Google Apps Script library. Ref
In this answer, I would like to propose a sample script using the library.
Usage:
1. Install Google Apps Script library.
Please install DocsServiceApp Google Apps Script library. You can see how to install it at here.
2. Sample script.
Please copy and paste the following script to the script editor of Spreadsheet. And, please set the variables of folderId and sheetName.
function myFunction() {
const folderId = "###"; // Please set folder ID you want to put the created files.
const sheetName = "Sheet1"; // Please set your sheet name.
// Retrieve image data.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const res = DocsServiceApp.openBySpreadsheetId(ss.getId()).getSheetByName(sheetName).getImages();
// Retrieve IDs from from column "B"
const folder = DriveApp.getFolderById(folderId);
const sheet = ss.getSheetByName(sheetName);
const values = sheet.getRange("B1:B" + sheet.getLastRow()).getValues();
// Create files.
res.forEach(({ range, image }) =>
folder.createFile(image.blob.setName(`${values[range.row - 1][0]}.png` || image.blob.getName()))
);
}
When this script is run, the image data is retrieved and created as the image file using the filename retrieved from column "B".
Note:
In this sample script, from your provided sample image, it supposes that the image data and the filename are put in the columns "O" and "B", respectively. Please be careful about this.
Reference:
DocsServiceApp

Checking if a filename exists and updating the file it in Google Script

I currently have a script which merges data from a Google Sheet into a Google Doc template. For each row of the worksheet, a new document is created using the title data from the row. The script works fine, but isn't my work. It has been passed onto me and I'm not skilled enough at Google Script to figure out what I'd like to achieve.
Ideally I wanted to know if it was possible to check when the script is run whether the document file already exists. It would do this as each document that is created uses the title data from the worksheet. If the document does exist then the data could be updated in that sheet, rather than creating a new version of it.
The script is the following
function mergeDocSheet() {
const TEMPLATE_ID = '16YfyeDjGDp-88McAtLCQQyZ1xz4QX5z';// Google Doc template ID
const SS_ID = '1C5gtJCSzHMuSz-oVWEItl2EUVRDwF5iH_'; // Google Sheet ID
const SHEET_NAME = "data"; // Google Sheet Tab name
const MAPPED = mappedDocToSheet;
const FILE_NAME = ["Titre de la formation"] // Header IDs from sheet.
docMerge(TEMPLATE_ID,SS_ID,SHEET_NAME,MAPPED, FILE_NAME);
}
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
//Get the Spreadsheet and sheet tab
const ss = SpreadsheetApp.openById(ssID);
const sheet = ss.getSheetByName(sheetName);
//Get number of rows to process
rowLen = (rowLen = "auto") ? getRowLen() - 1 : rowLen;
//Gets the range of data in the sheet then grabs the values of the range
const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
const matrix = range.getValues();
// Searches the file mapped object and finds the corresponding number returns the column number in an array.
const fileNameRows = getFileNameRows()
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row)
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
};
function updateFileData(rowArray, doc){
//Loops through the mapped object.
mapped.forEach(function(element){
let textID = `\{\{${element.doc}\}\}`
DocumentApp.openById(doc).getBody().replaceText(textID,
rowArray[element.col]);
});
};
function buildFileName(rowArry){
let fileNameArray = fileNameRows.map(ele => rowArry[ele]);
return fileNameArray.join("_");
};
function getFileNameRows(){
//Map the column indexes from fileNameData
let fileNameLocs = fileNameData
.flatMap(name => {
return mapped.filter(element => element.sheet === name)
.map(ele => ele.col);
});
return fileNameLocs;
};
function getRowLen(){
return sheet.getDataRange().getNumRows();
};
};
Would it be possible to set up some kind of conditional, perhaps around these lines?
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
I'm hoping someone can point me in the right direction with this. Any advice is much appreciated.
You can consider using searchFiles(params) to search for a specific filename with Doc type in your drive based on the search query term guidelines. Once you found all the files having the same filename, you can delete each file using setTrashed(trashed) before creating a new file using the template document
Sample Code:
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row);
//This query parameter will search for an exact match of the filename with Doc file type
let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
let files = DriveApp.searchFiles(params);
while (files.hasNext()) {
//Filename exist
var file = files.next();
///Delete file
file.setTrashed(true);
}
//Create a new file
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
};
In this given sample code, we will loop all files that have the exact filename and delete each file before creating a new one.
Additional References:
Google Drive Mimetypes

Creating PDFs from Spreadsheet Need PDF URL to be returned to Spreadsheet

I have a spreadsheet that contains data, a doc that is acting as my template and the start of a script that creates a PDF using the spreadsheet & doc for each line of the data. This works great!
My next step and this is where I am struggling, is I need to put the URL/identifier (from google drive) of each created PDF (each PDF goes to the same folder) into my spreadsheet so that I can then create another script to email the recipient their document.
I've seen this done when using a form and triggers but as I am not actively collecting the data, I cannot figure out this next part.
Thanks,
Nate
/*This function takes the data from the Charitable Tax Receipt summary and passes one row at a time
to the createPDF function.
*/
function createTaxRcptPDFs(){
const DOCTEMPLATE = DriveApp.getFileById("ref");
const TEMPFOLDER = DriveApp.getFolderById("ref1");
const PDFFOLDER = DriveApp.getFolderById("ref2");
const CURRENTSHEET = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("TEST");
const DATA = CURRENTSHEET.getRange(5,1,3,20).getDisplayValues(); //change 3 to formula when testing done
// this code block creates a data array to populate each PDF per (row)
DATA.forEach(row => {
const ACTIVECELL = row[20];
createPDF(row[2],row[4],row[5],row[6],row[7],row[1],row[14],row[15],row[16],new Date(),row[1] + "_" + row[2],DOCTEMPLATE,TEMPFOLDER,PDFFOLDER);
});
}
/* This function creates the PDF based on a Doc template and saves it to a specific folder
for future use.
*/
function createPDF(fullName,street,city,state,postalCode,receiptNumber,donation,advantage,eligible,rcptDate,pdfName,DOCTEMPLATE,TEMPFOLDER,PDFFOLDER){
// constants/variables to use
const TEMPFILE = DOCTEMPLATE.makeCopy(TEMPFOLDER);
const TEMPDOCFILE = DocumentApp.openById(TEMPFILE.getId());
// array of values to create body from data
const BODY = TEMPDOCFILE.getBody();
BODY.replaceText("{fullName}", fullName);
BODY.replaceText("{street}", street);
BODY.replaceText("{city}", city);
BODY.replaceText("{state}", state);
BODY.replaceText("{postalCode}", postalCode);
BODY.replaceText("{receiptNumber}", receiptNumber);
BODY.replaceText("{donation}", donation);
BODY.replaceText("{advantage}", advantage);
BODY.replaceText("{eligible}", eligible);
BODY.replaceText("{rcptDate}", rcptDate);
TEMPDOCFILE.saveAndClose();
// create pdf and delete temp file
const PDFBLOB = TEMPFILE.getAs(MimeType.PDF);
PDFFOLDER.createFile(PDFBLOB).setName(pdfName);
TEMPFOLDER.removeFile(TEMPFILE);
}
Solution:
setName(pdfName) returns a File object for chaining, so you can use getDownloadUrl() right after.
var fileUrl = PDFFOLDER.createFile(PDFBLOB).setName(pdfName).getDownloadUrl();
Reference:
getDownloadUrl()

google script import csv from ftp o google drive

i'm using a script to import a csv from a link but there is a way to adapt it for import from an ftp link or google drive file?
thank's
this is my script
function myFunction3() {
// 1. Set the required columns as the column number.
const requiredColumns = [1, 5, 20]; // Please set the required columns. These values are from your question.
// 2. Retrieve CSV data from an URL.
const url = "https://www.stanem.it/csv/InnovaCSV.csv";
const res = UrlFetchApp.fetch(url);
// 3. Convert CSV data to Spreadsheet.
const id = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS, title: "tempSpreadsheet"}, res.getBlob()).id;
// 4. Delete the columns except for the required columns.
const ss = SpreadsheetApp.openById(id);
const sheet = ss.getSheets()[0];
const maxColumn = sheet.getMaxColumns();
const requests = [];
for (let i = 1; i <= maxColumn; i++) {
if (!requiredColumns.includes(i)) {
requests.push({deleteDimension: {range: {sheetId: sheet.getSheetId(), dimension: "COLUMNS", startIndex: i - 1, endIndex: i}}});
}
}
Sheets.Spreadsheets.batchUpdate({requests: requests.reverse()}, id);
// 5. Copy the values of modified CSV data to a sheet in the active Spreadsheet.
const destinationSheetName = "Sheet1"; // Please set the destilnation sheet name in the active Spreadsheet.
const dstss = SpreadsheetApp.getActiveSpreadsheet();
const values = Sheets.Spreadsheets.Values.get(id, sheet.getSheetName()).values;
Sheets.Spreadsheets.Values.update({values: values}, dstss.getId(), destinationSheetName, {valueInputOption: "USER_ENTERED"});
// 6. Remove the temporat Spreadsheet.
DriveApp.getFileById(id).setTrashed(true);
}
Issue and workaround:
Unfortunately, in the current stage, the file cannot be retrieved from FTP using UrlFetchApp. So in your situation, how about retrieving the file from Google Drive? When the CSV file is retrieved from Google Drive, the modified script is as follows.
Modified script:
Please modify your script as follows. In this modified script, it supposes that the CSV file is put in your Google Drive.
From:
// 2. Retrieve CSV data from an URL.
const url = "https://www.stanem.it/csv/InnovaCSV.csv";
const res = UrlFetchApp.fetch(url);
// 3. Convert CSV data to Spreadsheet.
const id = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS, title: "tempSpreadsheet"}, res.getBlob()).id;
To:
// 3. Convert CSV data to Spreadsheet.
const fileId = "### fileId of CSV file on Google Drive ###"; // Please set the file ID of CSV file.
const id = Drive.Files.copy({mimeType: MimeType.GOOGLE_SHEETS, title: "tempSpreadsheet"}, fileId).id;
In this case, the CSV file can be converted with Drive.Files.copy.
Reference:
Files: copy