App Script - Exporting Sheets Hyperlinks to Docs - google-sheets

I have a google sheet - and when a new row appears I am writing the output into a Google Document using a predefined template via a merge.
All is working but as I could only work out how to use the .replaceText() function to achieve the merge, the hyperlinks in some of the sheet columns get exported as plain text.
After much fiddling and cribbing of code (thanks all) I managed to cobble together the following function:
function makeLinksClickable(document) {
const URL_PATTERN = "https://[-A-Za-z0-9+&##/%?=~_|!:,.;]+[-A-Za-z0-9+&##/%=~_|]"
const URL_PATTERN_LENGTH_CORECTION = "".length
const body = document.getBody()
var foundElement = body.findText(URL_PATTERN);
while (foundElement != null) {
var foundText = foundElement.getElement().asText();
const start = foundElement.getStartOffset();
const end = foundElement.getEndOffsetInclusive() - URL_PATTERN_LENGTH_CORECTION;
const url = foundText.getText().substring(start,end+1)
foundText.setLinkUrl(url)
foundElement = body.findText(URL_PATTERN, foundElement);
}
}
After writing out all the columns to the document I call this function on the created document to look for a hyperlink and make it hyper :)
As long as each cell only contains one hyperlink my function works.
It also works where there are multiple hyperlinks in the document.
However, some cells can have multiple hyperlinks and writes them out to the document with a new line for each one.
Although the function finds the multiple URLs correctly and makes them clickable in the document there is a problem.
For example, if there are 2 hyperlinks in the cell they get exported to 2 lines in the document, but after running them through the function - both hyperlinks will now link to the same image (the first) even though each hyperlink itself is the unique link from the original cell.
2 converted hyperlinks that link to the same image
(Note - If I don't run my function and leave the exported hyperlinks as text. Then go into the created document and manually add a space to the ends of the exported hyperlinks then they turn blue and become clickable and link to the correct image, I did try to add a space programmatically before this but couldn't work that out either)
I have exhausted my limited coding ability and can't see why my function which "seems" to work its way through each hyperlink correctly doesn't make it then link to the right image in the document.
Any help would be most appreciated.
Thanks
// ----------------------------------------------------------------------
Thank you for taking the time to look at this, I will try to explain the issues further. It is hard to show here as the links actually work properly when copied here they only misbehave in the google document.
A cell in the exported row has multiple hyperlinks separated by a comma.
they get exported from the cell to the document as text strings like this:
Links in single Sheets Cell for exporting:
"hyperlink-1-as-a-string", (links to image 1)
"hyperlink-2-as-a-string", (links to image 2)
"hyperlink-3-as-a-string", (links to image 3)
"hyperlink-4-as-a-string", (links to image 4)
"hyperlink-5-as-a-string" (links to image 5)
I then run my funtion to make them clickable again.
If there are two are more hyperlinks in the same cell when exported then I get the following issue after running the function.
Exported Text links converted by to clickable hyperlinks:
"hyperlink-1-as-a-string", (links to image 5)
"hyperlink-2-as-a-string", (links to image 5)
"hyperlink-3-as-a-string", (links to image 5)
"hyperlink-4-as-a-string", (links to image 5)
"hyperlink-5-as-a-string" (links to image 5)
I "think" what happens is that my function makes all 5 hyperlinks one big hyperlink that happens to use the last hyperlinks image.
If I copy and paste the URLs into a separate document like an email then they appear as one large hyperlink, not 5 separate ones.
// ---------------------------------------------------------------
The function searches for text patterns that are in fact google hyperlinks.
(starting https:// etc)
When it finds one it works out the length to the end of the text string and then uses setLinkUrl() to make the hyperlink - into a clickable hyperlink.
If there is only one text hyperlink then it works.
If there is more than one text hyperlink, separated by commas then it does not.

I worked something out. This is what I ended up with, it is basically put together from a few other questions & answers - It's not very clever but it works.
Thanks to the various posters who enabled me to figure this out.
function sortLinks(colId, mapPoint, myBody) {
var urls = [];
if (colId.includes(",")) { // IE theres more than one URL
var tmp = colId.split(",");
urls = urls.concat(tmp);
}
else {
urls[0] = colId; // 1 URL no "," add to array[0]
}
if (urls.length > 0) {
var tag = mapPoint;
var newLine = "\n";
var element = myBody.findText(tag);
if (element) {
var start = element.getStartOffset();
var text = element.getElement().asText();
text.deleteText(start, start + tag.length - 1);
urls.forEach((url, index) => {
url = url.trim();
var name = "Image-Video" + (index + 1);
text.appendText(name).setLinkUrl(start, start + name.length - 1, URL);
text.appendText(newLine);
start = start + name.length + newLine.length;
});
}

Related

How do I use the split function in Google Sheets correctly?

I want to get data from the text found in 2.. In 1. Is the data I have available and in 3. You will find the wanted result.
I have the following information available
Categories
Kleur
Soorthek
Bevestigingswijze
I am getting this text from scraping: BevestigingswijzeKlembevestigingKleurWitSoorthekSpijlenhek
I want this as a result by using a function in Google Sheets.
Wanted Result
KleurWit
SoorthekSpijlenhek
BevestigingswijzeKlembevestiging
Thank you in advance!
You could accomplish this with an Apps Script custom function. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function splitScrape(categories, scrape) {
const indexes = categories.map(c => scrape.indexOf(c[0])).sort();
const split = indexes.map((index, j) => scrape.slice(index, indexes[j+1]));
return split;
}
The sample above won't detect multiple occurrences of the same category in the scrape string, and it will only handle the first one (if a second parameter is not provided, indexOf only detects the first occurrence). In order to detect multiple occurrences, I'd suggest replacing the function with this one:
function splitScrape(categories, scrape) {
const indexes = [];
categories.flat().filter(String).forEach(c => {
let index = scrape.indexOf(c);
while (index > -1) {
indexes.push(index);
index = scrape.indexOf(c, index + 1);
}
});
indexes.sort((a, b) => a-b);
const split = indexes.map((index, j) => scrape.slice(index, indexes[j+1])).filter(String);
return split;
}
Now, if you go back to your spreadsheet, you can use this function like any in-built one. You just have to provide the appropriate ranges where the Categories and the scrape string are located, as you can see here:
Reference:
Custom Functions in Google Sheets

How to find the right xpath for Google sheets?

I would like to scrape data from a page, but cannot figure out the right xpath for Google sheets. I would like to extract the number 202 from https://www.belvilla.nl/zoeken/?land=nl&rgo=frie (on top of the page, "202
vakantiehuizen gevonden in Friesland")
If I take the xpath, I get: //*[#id="result-container-items"]/div[1]/div/div/div[1]/div[1]/div[1]/strong
In Google sheets I have tried =IMPORTXML(A1;"//*[#id="result-container-items"]/div[1]/div/div/div[1]/div[1]/div[1]/strong)") and some others like =IMPORTXML(A1;"//div[#class='search-numbers']"), but none of them are working. For the last one I get an error with 'Resource with URL content has exceeded the size limit.' but I'm guessing my xpath is wrong.
Can anyone help me out? Thanks!
IMPORTXML has its limitations especially on JS elements. However, if scripting is an option, try using UrlFetchApp.fetch() in Google Apps Script.
Code:
function fetchUrl(url) {
var html = UrlFetchApp.fetch(url).getContentText();
// startString and endString must be unique or at least the first result
// enclosing the result we want
var startString = 'search-result-div" ';
var endString = 'alternate-dates-filter-bar';
var startIndex = html.search(startString);
var endIndex = html.search(endString);
// regex for numbers and text content
var numbers = /strong>([^<]+)<\/strong/;
var text = /span>([^<]+)<\/span/;
// clean content then combine matches of numbers and text
var content = html.substring(startIndex, endIndex).replace(/\s\s+/g, ' ');
var result = numbers.exec(content)[1] + ' ' + text.exec(content)[1];
return result.trim();
}
Output:
Note:
Code above is specific to what you are fetching. You will need to update the script processing of the response if you want anything else.
You can reuse this on other url and will fetch the similar value located on your wanted xpath in your post.
This doesn't make use of the xpath.
google sheets do not support the scraping of JavaScript elements. you can check this if you disable JS for a given URL and you will be left with content you could import. in your case, this cant be achieved with IMPORTXML:

google sheet: make a local copy of image link from a shared sheets

my frient shared his google sheet to me and the table contains image which is a link (url). How can i make a copy of this sheet and make all the image link to be local, so i want the image is copying to my local google drive automatically (so the link won't be broken if he delete his images files in future). Right now, if i make a copy of this document, then it still link to original image source.
How is it possible ? of course i don't want to manually copy them one by one from the link. Is there any better and faster way ?
https://docs.google.com/spreadsheets/d/1TkXwAd8rKbjnGfYEJVaOYBJwCZ7G7YfuSvmcDE6g8No/edit?usp=sharing
The OP wants to extract the image URL from a hyperlink formula, and save a copy of the image to their own Google Drive account.
This answer combines several elements from precedents on StackOverflow.
Since the images metadata is in the formula, the code uses the getFormulas() method rather than the "conventional" getValues(). Cells with no formula are empty strings; hence the test if (formula.length !=0){.
Get the file name without extension: REGEX: Capture Filename from URL without file extension. Ironically, this precedent doesn't use regular expressions but finds the position of the last / and the last . using lastIndexOf and getting a substring between those points. Note this solution fails on filenames with multiple periods, though there is an alternative solution for this scenario.
Get the file name from the url: Getting a Google Spreadsheet Cell's Image URL which combines regex and Javascript match.
Save a file to Google Drive: Need sheets script to save img to drive which is a simple and elegant solution for saving files.
Saving the file to Google Drive: When copying files using Apps Script from one folder to another any “Apps Script” files being copied end up in MyDrive not the specified folder - why? explains why the API is required to write the files to My Drive.
Note: In order to use this script, enable Drive API v2 at Advanced Google Services
On script editor, Resources -> Advanced Google Services; Turn on Drive API v2
function so5811567402() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheetName = "Table";
var sh = ss.getSheetByName(sheetName);
var rg=sh.getDataRange();
var lastColumn = sh.getLastColumn();
var lastRow = sh.getLastRow();
var formulas = rg.getFormulas();
for (var i in formulas) {
for (var j in formulas[i]) {
var formula = formulas[i][j];
if (formula.length !=0){
var regex = /image\("(.*)"/i;
var matches = formula.match(regex);
var imgurl = matches[1];
var filename = imgurl.substring(imgurl.lastIndexOf("/") + 1, imgurl.lastIndexOf("."));
//Logger.log(filename);
var image = UrlFetchApp.fetch(imgurl).getBlob().getAs('image/jpeg').setName(filename);
var FolderId = "Folder ID goes here";
var folder = DriveApp.getFolderById(FolderId);
var file = DriveApp.createFile(image);
Drive.Files.update({"parents": [{"id": folder.getId()}]}, file.getId());
}
}
}
}

Can you write a Google Sheets function that draws something?

Is it possible to write your own custom function in google sheets script that returns a drawn image, similar to how the SPARKLINE function works, except I want to make one that draws a pie chart instead.
I do not want to use Insert > Chart... > Pie Chart because that creates a floating chart on top of the spreadsheet. I would like to be able to write my own function that would return a pie chart that is embedded within the cell that the function is entered in, just like you can do with columns, bars, and line charts using sparkline.
How about following idea? This sample script embeds a chart to a cell using custom function on Spreadsheet. I think that this method is one of various ideas.
Problems :
When you want to create a chart and embed it to a cell using custom functions, you notice that insertChart() cannot be used. There are some limitations for using custom functions. But insertChart() creates floating charts. So in order to embed a chart to a cell, the function =IMAGE() is suitable for this situation. Here, setFormula() for setting =IMAGE() and DriveApp.createFile() for creating images from charts also cannot be used for custom functions.
Solution :
In order to avoid these limitations, I used Web Apps.
To use this sample script, please deploy Web Apps as follows.
On the Script Editor,
File
-> Manage Versions
-> Save New Version
Publish
-> Deploy as Web App
-> At Execute the app as, select "your account"
-> At Who has access to the app, select "Anyone, even anonymous"
-> Click "Deploy"
-> Copy "Current web app URL"
-> Click "OK"
When it deploys Web Apps, the approval required authorization can be done, simultaneously.
Sample Script :
Please copy and paste this script to a bound script of spreadsheet.
var folderId = "### Folder ID ###"; // This is a folder to save images.
var webappsurl = "https://script.google.com/macros/s/######/exec"; // Here, please put "Current web app URL".
function embedChart(range) {
var ac = SpreadsheetApp.getActiveSheet().getActiveCell();
var q1 = "?datarange=" + range;
var q2 = "&row=" + ac.getRow();
var q3 = "&col=" + ac.getColumn();
var url = webappsurl + q1 + q2 + q3;
UrlFetchApp.fetch(url);
}
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var chart = sheet.newChart()
.setChartType(Charts.ChartType.PIE)
.addRange(sheet.getRange(e.parameters.datarange))
.setOption('height', 280)
.setOption('width', 480)
.setOption('title', 'Sample chart')
.build();
var file = DriveApp.getFolderById(folderId).createFile(
chart.getAs('image/png').setName("chart_image.png")
);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
sheet.getRange(e.parameters.row, e.parameters.col).setFormula(
'=IMAGE("' + "http://drive.google.com/uc?id=" + file.getId() + '")'
);
}
Flow of Script :
embedChart()
Input =embedChart("a2:a6") in cell B7.
Using fetch(), sends data of a2:a6 and the inputted coordinate to doGet().
doGet()
Using doGet(), get the data.
Creates a chart using inputted range a2:a6. (in this case, creates a pie chart)
Saves a chart as an image. (in this case, saves as PNG)
Updates a permission of the image file to use for =IMAGE().
Embeds the image using =IMAGE() which was imported by setFormula().
Result :
By inputting =embedChart("a2:a6") in cell B7 as a custom function, following result can be obtained.
Note :
When the custom function embedChart() is used, loading time is about 40 seconds. (I don't know whether this occurs at only my environment.)
Permissions of the created image are ANYONE_WITH_LINK, VIEW.
embedChart() is overwritten by =IMAGE(). So when the spreadsheet is reopened, the response of =IMAGE() is much faster than that of embedChart().
If I misunderstand your question, I'm sorry.

Google spreadsheet direct download link for only ONE sheet as excel

I was wondering if its possible to download say only sheet 1 of a google spreadsheet as excel? I have seen few SO posts that show the method to export the WHOLE sheet as excel, but I need to just export one sheet. Is it at all possible? and if yes, how?
You can download a specific sheet using the 'GID'.
Each sheet has a GID, you can find GID of specific sheet in the URL of
spreadsheet. Then you can use this link to download specific sheet -
https://docs.google.com/spreadsheets/d/<KEY>/export?format=xlsx&gid=<GID>
ex:
https://docs.google.com/spreadsheets/d/1D5vzPaOJOx402RAEF41235qQTOs28_M51ee5glzPzj0/export?format=xlsx&gid=1990092150
KEY is the unique ID of the spreadsheet.
source: https://www.quora.com/How-do-I-download-just-one-sheet-from-google-spreadsheet/answer/Ranjith-Kumar-339?srid=2YCg
From what I've found, the other two answers on this post are exactly correct, all you need to do is replace this:
/edit#gid=
with:
/export?format=xlsx&gid=
This works just fine although I did find that I had to keep looking up this string and copying it. Instead, I made a quick Javascript snippet that does all the work for you:
Just run the code snippet below and drag the link it creates into your bookmarks bar. I know this is a little hacky but for some reason, stackoverflow doesn't want me injecting javascript into the links I provide.
Export Sheet as Excel
I've tested this on the latest versions of Chrome, Safari, and Firefox. They all work although you might have to get a little creative about how you make your bookmarks.
when you see every Google spreadsheet url looks like this
https://docs.google.com/spreadsheets/d/1D5vzPaOJOx402RAEF41235qQTOs28_M51ee5glzPzj0/edit#gid=1078561300
In every spreadsheet URL we can see: /edit#gid=
this is generally the default mode.
/edit#gid=
just replace it with:
/export?format=xlsx&gid=
it will download the single spreadsheet from the workbook
I am able to download all sheets of a spreadsheet.
Just remove anything after
/edit?
and replace with
/export?format=xlsx
for Excel
or
/export?format=pdf
for PDF
Please use any_value() function before the column because field(column) have more than one value for one id(group by).
like-
select any_value(phone_no) from user_details group by user_id.
here one user_id have more than one phone number so query confused which choose.
You can do this by clicking on the down arrow near the sheet name to bring up the options, and then selecting "Copy to -> New spread sheet", then click the "Open spread sheet" in the pop up that comes up after.
You can use my code:
function emailAsExcel() {
var config = {
to: "name#gmail.com",
subject: "your text",
body: "your text"
};
var ui = SpreadsheetApp.getUi();
if (!config || !config.to || !config.subject || !config.body) {
throw new Error('Configure "to", "subject" and "body" in an object as
the first parameter');
};
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = spreadsheet.getId();
var file = Drive.Files.get(spreadsheetId);
var url = 'https://docs.google.com/spreadsheets/d/'+spreadsheetId+'/export?
format=xlsx&gid=numberSheetID to email';
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
};
});
var fileName = (config.fileName || spreadsheet.getName()) + '.xlsx';
var blobs = [response.getBlob().setName(fileName)];
if (config.zip) {
blobs = [Utilities.zip(blobs).setName(fileName + '.zip')];
}
GmailApp.sendEmail(
config.to,
config.subject,
config.body,
{
attachments: blobs
}
);
}

Resources