Google Visualization.Sankey -- GoogleScript Dropping Node Label - google-apps-script

So I am able to make a Sankey plot from my Google Sheet data using Google Chart. The issue is that the plot seems to drop off the label for one of the nodes.
My data looks as follow:
Source Type Count
External A 12
External B 7
External C 1
External D 0
Internal A 26
Internal B 23
Internal C 15
Internal D 0
Other A 0
Other B 1
Other C 24
Other D 0
The plot looks like
As you can see the last Node name "C" (in green is dropped)
My GoogleScript code was as follow:
code.gs
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('CustomScripts')
.addItem('SankeyDiagram', 'openDialog')
.addToUi();
}
function getSpreadsheetData() {
// ID of your Document, take from URL
var ssID = "sheetID",
sheet = SpreadsheetApp.openById(ssID).getSheets()[0],
data = sheet.getDataRange().getValues();
return data;
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setHeight(300)
.setWidth(1000);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sankey Diagram');
}
And the corresponding HTML was:
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://www.google.com/jsapi"></script>
</head>
<body>
<div id="main"></div>
<script type="text/javascript">
google.load('visualization', '1', {
packages: ['corechart', 'sankey']
}); google.setOnLoadCallback(initialize);
function initialize() {
google.script.run.withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(rows) {
console.log(rows);
var data = google.visualization.arrayToDataTable(rows);
var chart = new google.visualization.Sankey(document.getElementById('sankey_basic'));
chart.draw(data, {'title':'Detection Method by Severity',width: 900,height: 250, sankey: {}});
google.script.run.withSuccessHandler().newChart(chart);
}
</script>
</body>
</head>
<body>
<div id="sankey_basic" style="width: 900px; height: 300px;"></div>
</body>
</html>
What am I overlooking that is causing the "C" category not to appear?
Separate but not critical question -- is there a way to embed the image as an image/chart on the active sheet
Also for the code reference... special thanks to
njoerd114 for creating the framework for drawing Sankey plots... https://gist.github.com/njoerd114/839b9a5298843ea4cf9fd241e39ebbf6

Suggestion:
The reason why you are getting the cut image of the sankey graph is because of the zero values you have in "D".
I have found a similar concern like this in git hub: Node positions do not work with link value zero in Sankey
I suggest that you remove zeros first by filtering the array:
Modified code:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('CustomScripts')
.addItem('SankeyDiagram', 'openDialog')
.addToUi();
}
function getSpreadsheetData() {
// ID of your Document, take from URL
var ssID = "1KtvAFFQV13zSwktXKtUXZLQF9hmb9VaxbC4OM3hGEVw",
// which Sheet? [0] is the first and so on...
sheet = SpreadsheetApp.openById(ssID).getSheets()[0],
data = sheet.getDataRange().getValues(),
///Filtered the data by removing zeros
header = sheet.getRange(1,1,1,3).getValues()[0],
filteredData = data.filter(r => r[2] > 0);
filteredData.unshift(header)
return filteredData
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setHeight(300)
.setWidth(1000);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sankey Diagram');
}
Result:

Related

How to get the selected chart in a Google spreadsheet with Apps Script

I am trying to change the color of charts in Google spreadsheet. This is working for all charts in one sheet.
function colorChart() {
let $Sheet = SpreadsheetApp.getActiveSheet();
let $Charts = $Sheet.getCharts();
let $Colors = {
'One': '#6E6E6E',
'Two': '#FFED00',
'Three': '#238C96',
};
for (let $i in $Charts) {
let $Chart = $Charts[$i].modify()
.setOption('series.0.color', $Farben['One'])
.setOption('series.1.color', $Farben['Two'])
.setOption('series.2.color', $Farben['Three'])
.build();
$Sheet.updateChart($Chart);
}
}
In the documentation I can only find getCharts() which returns an array of all charts in the active sheet.
Is it possible to get the selected chart only?
Unfortunately the question was not answered here: How to get the chart info from selected chart in active spreadsheet? (google apps script)
Using $Sheet.getCharts()[0] as mentioned in the post was not working since only the first chart of the sheet was returned.
SUGGESTION/WORKAROUND
The challenge here is that SpreadsheetApp class in Apps Script doesn't seem to have a dedicated method to get the selection of a chart or any movable/re-sizeable elements on a sheet but only for selected cells/rows.
As a workaround, perhaps you can try an implementation using a custom HTML page that will give you an option to view currently created charts from the sheet (via custom menu & custom sidebar) and from there, you will be able to have an option to select a chart from that HTML interface & apply the changes.
Demo
Sample: Edit the Pie Chart
Sample drop-down menu on the HTML page (due to limitations of drop-down on screen capture)
Sample apps script file & HMTL file:
NOTE: Community members don't code for you but you can use these sample codes (derived from several sample codes scoured over the internet) below to get you started.
Code.gs
/**Creates a custom menu to load an edit chart sidebar.*/
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Edit Chart')
.addItem('Start', 'showSidebar')
.addToUi();
}
/**Shows the custom html sidebar.*/
function showSidebar() {
var html = HtmlService.createHtmlOutputFromFile('Page')
.setTitle('Edit Charts');
SpreadsheetApp.getUi()
.showSidebar(html);
}
/**Function to return the current charts on a sheet.*/
function getCurrentChartsOnSheet() {
var charts = buildMenu();
return charts;
}
/**Function to get the current charts on a sheet.*/
function buildMenu() {
var getChartsMenu = [];
let $Sheet = SpreadsheetApp.getActiveSheet();
let $Charts = $Sheet.getCharts();
for (let $i in $Charts) {
var currentChart = $Charts[$i].getOptions().get('title');
getChartsMenu.push([$i + ' - ' + (currentChart.length < 1 ?
$Charts[$i].modify().getChartType().toString() : currentChart)])
}
return getChartsMenu; //Structure of the array will be: [chart index #] - [Chart type]
}
/**Function to edit the selected chart.*/
function editSelectedChart(selection) {
let $Sheet = SpreadsheetApp.getActiveSheet();
let $Charts = $Sheet.getCharts();
let $Colors = {
'One': '#6E6E6E',
'Two': '#FFED00',
'Three': '#238C96',
};
let $Chart = $Charts[selection.match(/\d+/g)].modify()
.setOption('series.0.color', $Colors['One'])
.setOption('series.1.color', $Colors['Two'])
.setOption('series.2.color', $Colors['Three'])
.build();
$Sheet.updateChart($Chart);
}
Page.html
<!DOCTYPE html>
<html>
<title>Edit Charts</title>
<head>
<style>
.space {
width: 4px;
height: auto;
display: inline-block;
}
</style>
<script>
google.script.run.withSuccessHandler(onSuccess).getCurrentChartsOnSheet();
function onSuccess(data) {
var select = document.getElementById("selectChart");
var options = data;
for(var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
}
function passSelectedChart(){
var e = document.getElementById("selectChart");
google.script.run.editSelectedChart(e.value); //<-- PASS the selected drop-down data to GOOGLE SCRIPT function named editSelectedChart()
}
</script>
</head>
<body>
<h3>Choose a chart to edit:</h3>
<select id="selectChart">
<option>No Selection</option>
</select>
<div>
<p><input type="button" value="Apply" onclick="passSelectedChart()"></p>
</div>
<div>
<p><input type="button" value="Close" onclick="google.script.host.close()"></p>
</div>
</body>
</html>
References
Apps Script Custom sidebars
Apps Script Custom Menu
Class HtmlService
HTML DOM Element appendChild()
google.script.run (Client-side API)

Make a google Script Run from a HTML front

I have a script that will run an HTTP request to our server and bring the most recent orders.
We want to be able to run the script on request but also be able to Install it as an addon to different sheets for our different stores
The front End of the app is generated by this html
<link href="https://ssl.gstatic.com/docs/script/css/add-ons.css"
rel="stylesheet">
<div class="sidebar">
<div class="block form-group">
<button class="blue" id="load_orders">Import Order Data</button>
</div>
<div id='orders'></div>
</div>
<script>
$(function onSuccess(load_orders) {
});
withSuccessHandler(onSuccess).importcogs();
});
</script>
Then on the .gs I have a script to show the app (before we Deploy it) and the import orders script
function onInstall() {
onOpen();
}
function onOpen() {
SpreadsheetApp.getUi()
.createAddonMenu() // Add a new option in the Google Docs Add-ons Menu
.addItem("Import Order Data", "showSidebar")
.addToUi(); // Run the showSidebar function when someone clicks the menu
}
function showSidebar() {
var html = HtmlService.createTemplateFromFile("Front")
.evaluate()
.setTitle("Import Order - Search"); // The title shows in the sidebar
SpreadsheetApp.getUi().showSidebar(html);
}
function importcogs() {
Logger.log("import begin");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var urlsheet = ss.getSheetByName("GetInfo");
var request = urlsheet.getRange(5,2).getValue();
Logger.log(request);
var response = UrlFetchApp.fetch(request);
Logger.log("download data finish");
Logger.log(response.getContentText());
var sheet = ss.getSheetByName("Data");
var obj = JSON.parse(response);
let vs = obj.data.map(o => Object.values(o));//data
vs.unshift(Object.keys(obj.data[0]));//add header
sheet.getRange(1,1,vs.length, vs[0].length).setValues(vs);//output to spreadsheet
}
I Haven't been able to link the "Import orders" button to the script for some reason .
As what Cooper has said in the above comment, it is not a standalone function:
Sample Usage:
google.script.run.withSuccessHandler(onSuccess).importcogs();
This will in return runs the importcogs function and will trigger onSuccess function if the function successfully finished. Parameters of the onSuccess will be the return of the function called importcogs if it has any.

How can I create a web app where I can read text from a sheet based on dropdown selections?

I am trying to create a role description generator which reads pre-written text from a Google Sheet and assembles it in blocks in a web app through selections (team, role, seniority level, etc.) in dropdown menus.
This is an example of what the data in the sheet looks like:
Team name
Team description
A-team
Description
B-team
Description
...
...
So far, for the team selection, I have created the dropdown menu which reads the data from the sheet, and pulls the names of each team into a dropdown list. But my problem is loading the corresponding team description text into the HTML page. I just can't seem to get it to work.
When pressing the generate button, what should happen is that the description for A-team is loaded, but instead I get [object MouseEvent].
Any suggestions? Thanks in advance! :)
Here's my code:
Code.gs
var url = "*spreadsheet URL*";
function doGet(e) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
//get the data for the dropdown list
function valuesForList(list) {
//define the data
var ss = SpreadsheetApp.openByUrl(url)
var teamsSheet = ss.getSheetByName('Data');
var lastRow = teamsSheet.getLastRow();
var teamsRange = teamsSheet.getRange(1, 3, lastRow, 1);
//create a named range
ss.setNamedRange('teamsList', teamsRange);
//get the values from the range
var listValues = ss.getRangeByName(list).getValues();
return listValues;
}
//the function to show the data on the index.html
function PostInfo (userInfo){
//load the data
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
var teamList = data.map(function(r){ return r[0]});
var teamDesc = data.map(function(r){ return r[1]});
var position = teamList.indexOf(userInfo.teams);
if(position > -1){
return teamDesc[position];
} else {
return "Unavailable";
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
<script>
function onListSuccess(list) {
var listLength = list.length;
for (i=0; i<listLength;i++) {
var dropdown = document.getElementById("teams");
var opt = document.createElement("option");
dropdown.options.add(opt);
opt.text = list[i][0];
opt.value = list[i][0];
}
}
function onListSelect(teamDesc){
var text = teamDesc.toString().split(",");
document.getElementById('est').innerHTML = text;
}
</script>
</head>
<body>
<div id="main">
<h1>Role Description Generator</h1>
<p>
<label for="teams">Team:</label>
</p>
<p>
<select name="teams" id="teams" tabindex="2"></select>
</p>
<button id="btn">Generate</button>
<div>
<label for="est">Team description:</label>
<p id="est" name="est"></p>
</div>
</div>
</body>
<script>
function populateList(){
google.script.run.withSuccessHandler(onListSuccess).valuesForList('teamsList');
}
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
window.addEventListener('load', populateList);
</script>
</html>
Modification points:
In your script, when the dropdown list is changed, doStuff() is run. But in this case, google.script.run.PostInfo(userInfo) runs only the function of PostInfo at Google Apps Script. By this, the returned value is not used.
And, when the button is clicked, onListSelect is run. But in this case, teamDesc of onListSelect(teamDesc) is the event object. By this, such value of [object MouseEvent] is shown. I thought that this might be the reason of your issue.
By the way, when I saw your Google Apps Script, I noticed that PostInfo has a modification point. When var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues(); is run, I think that an error occurs. Because ws2 is not declared. In your case, is that ws? I thought that this might be due to your miscopy.
When you want to show the value from PostInfo when the button is clicked, how about the following modification?
Modified script:
HTML&Javascript side:
From:
document.getElementById("teams").addEventListener("change", doStuff);
document.getElementById("btn").addEventListener("click", onListSelect);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.PostInfo(userInfo);
}
To:
document.getElementById("btn").addEventListener("click", doStuff);
function doStuff(){
var userInfo = {};
userInfo.teams = document.getElementById("teams").value;
google.script.run.withSuccessHandler(onListSelect).PostInfo(userInfo);
}
Google Apps Script side:
From:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws2.getLastRow(),2).getValues();
To:
var ws = ss.getSheetByName("Teams");
var data = ws.getRange(2,1,ws.getLastRow(),2).getValues();
Note:
In this modidication, it supposes that the Google Apps Script works fine and returns the correct values. Please be careful this.
Reference:
Class google.script.run

Adding rows based on a few variables

I'm looking for a function that can get inputs like - "project", "locale", "date" and the function will go to the relevant sheet in the spreadsheet based on the "project" value and will show the relevant rows based on the locale and date.
For example -
Spreadsheet X includes 3 sheets - Project 1, project 2 & summary.
In the summary sheet, I have 3 variables - project, locale & date. I also have a button of "show".
Once I'll click on the "show" the function will show me the data according the variables.
Here's a simple example that I think may be close to what you want. It includes a search button and a clear button and expects you to select a cell on the row of the summary table that you desire to see the data of. After selecting a row on the summary table you click the search button and the data from that project page for that location and date will be display on the side bar. If you wish to clear the data then press the clear button.
Code.gs:
function searchForDataSideBar()
{
var ui=HtmlService.createHtmlOutputFromFile('searchinfordata');
SpreadsheetApp.getUi().showSidebar(ui);
}
function searchForData()
{
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Summary');
var cell=sh.getActiveCell();
var row=cell.getRow();
var cols=sh.getLastColumn();
var rg=sh.getRange(row, 1, 1, cols);
var vA=rg.getValues();
var sh1=ss.getSheetByName(vA[0][0]);
var rg1=sh1.getDataRange();
var vA1=rg1.getValues();
var s='';
for(var i=1;i<vA1.length;i++)
{
var data=vA1[i];
var a=vA[0][1];
var b=data[0];
var c=vA[0][2].valueOf();
var d=data[1].valueOf();
if(a==b && c==d)
{
s+=Utilities.formatString('Project Name:%s<br />Location:%s<br />Date:%s<br />Data1:%s<br />Data2:%s</br>Data3:%s</br>Data4:%s</br>Data5:%s</br><hr>',vA[0][0],data[0],Utilities.formatDate(new Date(data[1]), Session.getScriptTimeZone(), "E MMM dd,yy"),data[2],data[3],data[4],data[5],data[6])
}
}
return s;
}
I used a sidebar to put the buttons on and to display your data. The name of my file was searchinfordata.html:
<!DOCTYPE html>
<html>
<head>
<title>Searching for Data</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function search()
{
google.script.run
.withSuccessHandler(found)
.searchForData();
}
function found(hl)
{
document.getElementById('found').innerHTML=hl;
}
function clearDiv()
{
document.getElementById('found').innerHTML='';
}
console.log('MyCode');
</script>
</head>
<body>
<div id="instr">Please Select a cell on the row in the summary tab that you wish to see the data for.</div>
<input type="button" value="Search" onClick="search();" />
<input type="button" value="Clear" onClick="clearDiv();" />
<div id="found"></div>
</body>
</html>
And here's the onOpen() function where you can build your menu or launch your sidebar.
function onOpen()
{
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Show Sidebar','searchForDataSideBar')
.addToUi();
searchForDataSideBar();//launches your sidebar whenever you open your spreadsheet.
}
This is what the Summary Tab looks like:
P1 Tab:
P2 Tab:
P3 Tab:
And Finally the Sidebar:

Initiate a download from google apps script

I added a new menu item to my spreadsheet using google apps script. This menu item creates a file, but I'd like for it to initiate the download of the file after creating it.
Is this possible?
Remember, this is not a web app, but a menu item in my spreadsheet.
Thanks
Edit:
Thanks to Serge insas' suggestion, the following simple script works perfectly, and opens a download window with the link I need:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [ {name: "Zip all CSVs", functionName: "saveAsCSV"} ];
ss.addMenu("CSV", csvMenuEntries);
};
function saveAsCSV() {
var folder = createCSVs(); // creates a folder with CSV for each Sheet
var zipFile = zipCSVs(folder, "DI.zip"); // creates a zip of all CSVs in folder
var ui = UiApp.createApplication().setTitle("Download");
var p = ui.createVerticalPanel();
ui.add(p);
p.add(ui.createAnchor("Download", zipFile.getDownloadUrl()));
SpreadsheetApp.getActive().show(ui)
}
EDIT : read the comments below, Zig Mandel is perfectly right when he points out the limitations of the "complicated" version, it was really a simple (and fun) exercice to show other methods.
I think you'll have to use an intermediate Ui as a popup to confirm the download.
After that there are 2 possible ways that I know, one is very simple and the other is quite cumbersome, make your choice, the code below shows both of them.
note : to use the complicated one you need to deploy your app (ie save a version and deploy as webapp), for the simple one just use it "as it is". (I show the simple in the code comments).
The code :
function onOpen() {
var menuEntries = [ {name: "test download", functionName: "downloadFile"}
];
var sheet = SpreadsheetApp.getActiveSpreadsheet();
sheet.addMenu("Utils",menuEntries);
}
function downloadFile(){
var file = DriveApp.createFile('test file', 'Some content in this file to test it');
var fileID = file.getId();
var fileName = file.getName();
var ui = UiApp.createApplication().setTitle('Download');
var url = ScriptApp.getService().getUrl()+'?&ID='+fileID+'&name='+fileName;
var p = ui.createVerticalPanel();
ui.add(p);
p.add(ui.createAnchor('click to download', url));
p.add(ui.createAnchor('or use this link ',file.getDownloadUrl()));// this is the simple one, just get the file you created and use getDownloadUrl()
SpreadsheetApp.getActive().show(ui)
}
function doGet(e){
var fileId = e.parameter.ID;
var fileName = e.parameter.name;
var fileString = DocsList.getFileById(fileId).getContentAsString();
return ContentService.createTextOutput(fileString).downloadAsFile(fileName);
}
PS : I had some fun writing this, the "complicated version" is really funny imho :-)
OP's answer is deprecated (in 2021), so I made a more general purpose one based on it.
Code.gs:
// Runs when the spreadsheet starts, adds a tab at the top
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Script Menu')
.addItem('Download a file!', 'dlFile')
.addToUi();
}
// Run when you click "Download a file!"
function dlFile() {
let file = DriveApp.getRootFolder().createFile('Hi.txt', 'Hello, world!');
// Create little HTML popup with the URL of the download
let htmlTemplate = HtmlService.createTemplateFromFile('Download.html');
htmlTemplate.dataFromServerTemplate = { url: file.getDownloadUrl() };
let html = htmlTemplate
.evaluate()
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Download');
};
Download.html:
<!DOCTYPE html>
<html>
<head>
<script>
let data = <?!= JSON.stringify(dataFromServerTemplate) ?>; // Stores the data directly in the javascript code
function downloadFile() {
document.getElementById("dlBtn").innerText = "Downloading..";
window.open(data.url, '_blank');
document.getElementById("dlBtn").disabled = true;
}
</script>
</head>
<body>
<button id="dlBtn" onclick="downloadFile()">Download</button>
</body>
</html>
Just adding to #dr-bracket's answer where I made some small additions to the scripts in an attempt to stop the browser from navigating away to a new tab.
I got the idea from:
Download a created Google Doc from a deployed web app (Google Apps Script)
Where #tanaike uses the google.script.run.withSuccessHandler class and method to create a popup prompt then closes and returns to your app on download. (May not popup if your browser settings are set to not pick download location.)
Code.gs:
// Runs when the spreadsheet starts, adds a tab at the top
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Script Menu')
.addItem('Download a file!', 'dlFile')
.addToUi();
}
// Run when you click "Download a file!"
function dlFile() {
let file = DriveApp.getRootFolder().createFile('Hi.txt', 'Hello, world!');
// Create little HTML popup with the URL of the download. Added filename to object. ~~~~~~~~~~~
let htmlTemplate = HtmlService.createTemplateFromFile('Download.html');
htmlTemplate.dataFromServerTemplate = { url: file.getDownloadUrl(), name: file.getName() };
let html = htmlTemplate
.evaluate()
.setWidth(400)
.setHeight(300);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Download');
};
// Added the following to satisfy the withSuccessHandler method: ~~~~~~~~~~~~~
function createDownloadUrl(data) {
return {
url: data.url,
name: data.name,
};
}
Download.html:
<!DOCTYPE html>
<html>
<head>
<script>
let data = <?!= JSON.stringify(dataFromServerTemplate) ?>; // Stores the data directly in
// the javascript code
function downloadFile() {
const dlBtn = document.getElementById("dlBtn");
dlBtn.innerText = "Downloading..";
// window.open(data.url);
// Replaced with:
// the url and name variables will be returned here from the
// code.gs function createDownloadEvent() after it runs successfully.
google.script.run
.withSuccessHandler(({ url, name }) => {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = name;
a.href = url;
a.target = "_blank";
a.click();
})
.createDownloadEvent(data);
dlBtn.disabled = true;
}
</script>
</head>
<body>
<button id="dlBtn" onclick="downloadFile()">Download</button>
</body>
</html>
Resources:
https://developers.google.com/apps-script/guides/html/reference/run#withsuccesshandlerfunction
Download a created Google Doc from a deployed web app (Google Apps Script)