A Script to Simplify Creating a SO Table - google-apps-script

How to create a markdown table from Google sheets?

This code allows you to copy data from your spreadsheet, redact it, align each column independently and then post it in to SO with the appropriate markdown to make a nice looking table.
The Code:
redact.gs:
function onOpen() {
menu();
}
function menu() {
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Authenticate','authenticate')
.addItem('Redactable Table','showRedactTableDialog')
.addToUi();
}
function authenticate() {
//no nothing
}
function getCSVDataRange() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rg=sh.getActiveRange();
const cols=rg.getWidth();
const datarange=rg.getA1Notation();
return {datarange:datarange,columns:cols};
}
function getRedactRangeList() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rgA=sh.getActiveRangeList().getRanges();
const redactrange=rgA.map(function(rg,i){return rg.getA1Notation();}).join(',');
return {redactrange:redactrange};
}
function showRedactTableDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('redacttable').setWidth(400).setHeight(200);
const h=userInterface.getHeight();
const w=userInterface.getWidth();
const title='Redactable Data Table';
userInterface.append(Utilities.formatString('<div id="dim">w:%s,h:%s</div>',w,h));
SpreadsheetApp.getUi().showModelessDialog(userInterface, title);
}
function getPresets() {
return {datarange:'',redactrange:'',delimiter:',',redactstring:'Redacted'};
}
function getTablePresets() {
return {datarange:'',redactrange:'',align:'c',redactstring:'Redacted',aligntext:""};
}
function testrdtable() {
redactableDataTable({"redactrange":"","cols":"3","col":"3","align":"l","aligntext":"rrr","datarange":"A1:C4","redactstring":"Redacted"})
}
function redactableDataTable(obj) {
Logger.log(JSON.stringify(obj));
const {datarange,redactrange,redactstring,align,aligntext}=obj;
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const drg=sh.getRange(datarange);
const vA=drg.getValues();
//new parameters
const dlm='|';
const dlmrow={l:':---',c:':---:',r:'---:'};
const aline=(aligntext.length>0)?aligntext:align;
if(redactrange) {
const rgA1=redactrange.split(',');
//Logger.log(rgA1);
const rgA=rgA1.map(function(A1,i){
return sh.getRange(A1);
});
const rowStart=drg.getRow();
const colStart=drg.getColumn();
//const rowEnd=drg.getRow()+drg.getHeight()-1;
//const colEnd=drg.getColumn()+drg.getWidth()-1;
rgA.forEach(function(rg,k){
var v=rg.getDisplayValues();
let row=rg.getRow();
let col=rg.getColumn();
v.forEach(function(r,i){
r.forEach(function(c,j){
vA[row-rowStart+i][col-colStart+j]=redactstring;//redact string
});
});
});
}
var tsv='';
var hdr=[vA.shift()];
//header row
hdr.forEach(function(r,i){tsv+=dlm;r.forEach(function(c,j){if(j>0)tsv+=dlm;tsv+=c;});tsv+=dlm;});
tsv+='\r\n';
//delimiter row
hdr.forEach(function(r,i){tsv+=dlm;r.forEach(function(c,j){if(j>0)tsv+=dlm;tsv+=dlmrow[aline[j%aline.length]];});tsv+=dlm;});
tsv+='\r\n';
//data table
vA.forEach(function(r,i){if(i>0){tsv+='\r\n';}tsv+=dlm;r.forEach(function(c,j){if(j>0){tsv+=dlm;}tsv+=c;});tsv+=dlm;});
let s=`Data:${datarange} - Redact:${redactrange}`;
var html=Utilities.formatString('<body><input type="button" value="Exit" onClick="google.script.host.close();" /><br /><textarea rows="1" cols="150" id="rngs">%s</textarea><br /><textarea rows="30" cols="150" id="tsv">%s</textarea></body>',s,tsv);
html+='<br /><input type="button" value="Exit" onClick="google.script.host.close();" />';
console.log(html);
var ui=HtmlService.createHtmlOutput(html).setWidth(1200);
SpreadsheetApp.getUi().showModelessDialog(ui, 'Table Markdown');
}
html code:
redacttable.hmtl:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
select,
input {
margin: 2px 5px 2px 0;
font-size: 12px;
}
#cols {
margin: 2px 5px 2px 5px;
font-size: 12px;
}
.bold {
font-weight: "bold";
}
</style>
</head>
<body>
<form name="form">
<br /><input type="text" id="dtrg" name="datarange" placeholder="Select Data Range" size="20" readonly /><input type="button" value="Data" onClick="getDataRange();" title="Select Data Range." /><input type="text" id="cols" name="cols" size="2" readonly />Cols
<br /><input type="text" id="rdrg" name="redactrange" placeholder="Select Redact Ranges" size="20" readonly /><input type="button" value="Redact" onClick="getRedactRangelist();" title="Select Redact Rangelist." />
<br /><span class="bold">Alignment</span>
<br /><select name="align"><option value="l">left</option><option value="c">center</option><option value="r">right</option></select>
<input type="text" id="alntxt" name="aligntext" placeholder="Align all columns with r,c,or l only" size="25" oninput="getLength();" /><input type="text" name="col" id="col" size="2" readonly />
<br /><input type="text" id="rs" name="redactstring" size="15" />Redact String
<br /><input type="button" value="Submit" onClick="processForm(this.parentNode);" />
</form>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(obj){
if(obj.datarange) {$('#dtrg').val(obj.datarange);}
if(obj.redactrange) {$('#rdrg').val(obj.redactrange);}
if(obj.align) {$('#aln').val(obj.align);}
if(obj.redactstring) {$('#rs').val(obj.redactstring);}
if(obj.aligntext){$('$alntxt').val(obj.aligntext);}
})
.getTablePresets();
});
function getLength() {
let s=$('#alntxt').val();
let all='rlc';
if(!all.includes(s[s.length-1])){
$('#alntxt').val(s.slice(0,-1));
}
$('#col').val($('#alntxt').val().length);
}
function getDataRange() {
google.script.run
.withSuccessHandler(function(obj){
$('#dtrg').val(obj.datarange);
$('#cols').val(obj.columns);
})
.getCSVDataRange();
}
function getRedactRangelist() {
google.script.run
.withSuccessHandler(function(obj){
$('#rdrg').val(obj.redactrange);
})
.getRedactRangeList();
}
function processForm(form) {
google.script.run.redactableDataTable(form);
}
console.log('My Code');
</script>
</body>
</html>
tableMarkdown.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>';
</head>
<body>
<input type="button" value="Exit" onClick="google.script.host.close();" /><br />
<textarea rows="1" cols="150" id="rngs"></textarea><br /><textarea rows="30" cols="150" id="tsv"></textarea>
<br /><input type="button" value="Exit" onClick="google.script.host.close();" />
<script>
$(function(){
google.script.run
.withSuccessHandler((robj)=>{
$("#tsv").val(robj.tsv);
$("#rngs").val(robj.rngs);
}).redactableDataTable(obj);
});
</script>
</body>
</html>
This script is also available here: https://sites.google.com/view/googlappsscript/table-utility
Demo:
Version 2
This version auto generates Column Letters and Row numbers which I find are useful in situations where you have no headers in your data because it helps to provide a frame for understanding where the table is located. Anyway play with it. It's not hard to figure out.
GS:
function showRedactTableDialog() {
var userInterface = HtmlService.createHtmlOutputFromFile('redacttable').setWidth(400).setHeight(250);
const h = userInterface.getHeight();
const w = userInterface.getWidth();
const title = 'Redactable Data Table';
userInterface.append(Utilities.formatString('<div id="dim">w:%s,h:%s</div>', w, h));
SpreadsheetApp.getUi().showModelessDialog(userInterface, title);
}
function redactableDataTable(obj) {
Logger.log(JSON.stringify(obj));
const { datarange, redactrange, headers, rows, redactstring, align, aligntext } = obj;
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const drg = sh.getRange(datarange);
const vA = drg.getDisplayValues().map((r => {
r.forEach(c => {
c.replace(/\r\n/, ' ')
});
return r;
}));
//new parameters
const dlm = '|';
const dlmrow = { l: ':---', c: ':---:', r: '---:' };
const aline = (aligntext.length > 0) ? aligntext : align;
if (redactrange) {
const rgA1 = redactrange.split(',');
//Logger.log(rgA1);
const rgA = rgA1.map(function (A1, i) {
return sh.getRange(A1);
});
const rowStart = drg.getRow();
const colStart = drg.getColumn();
//const rowEnd=drg.getRow()+drg.getHeight()-1;
//const colEnd=drg.getColumn()+drg.getWidth()-1;
rgA.forEach(function (rg, k) {
var v = rg.getDisplayValues();
let row = rg.getRow();
let col = rg.getColumn();
v.forEach(function (r, i) {
r.forEach(function (c, j) {
vA[row - rowStart + i][col - colStart + j] = redactstring;//redact string
});
});
});
}
Logger.log(headers);
if (headers && headers.length > 0) {
vA.unshift(headers.split(','));
}
var tsv = '';
var hdr = [vA.shift()];
//header row
hdr.forEach(function (r, i) { tsv += dlm; r.forEach(function (c, j) { if (j > 0) tsv += dlm; tsv += c; }); tsv += dlm; });
tsv += '\r\n';
if(rows && rows.length > 0) {
tsv = dlm + tsv + dlm + ':---:';
}
//delimiter row
hdr.forEach(function (r, i) { tsv += dlm; r.forEach(function (c, j) { if (j > 0) tsv += dlm; tsv += dlmrow[aline[j % aline.length]]; }); tsv += dlm; });
tsv += '\r\n';
//data table
if(rows && rows.length > 0) {
let rA = rows.split(",");
vA.forEach(function (r, i) { if (i > 0) { tsv += '\r\n'; } tsv += dlm + rA[i] + dlm; r.forEach(function (c, j) { if (j > 0) { tsv += dlm; } tsv += c; }); tsv += dlm; });
} else {
vA.forEach(function (r, i) { if (i > 0) { tsv += '\r\n'; } tsv += dlm; r.forEach(function (c, j) { if (j > 0) { tsv += dlm; } tsv += c; }); tsv += dlm; });
}
let s = `Data:${datarange} - Redact:${redactrange}`;
var html = Utilities.formatString('<body><input type="button" value="Exit" onClick="google.script.host.close();" /><br /><textarea rows="1" cols="150" id="rngs">%s</textarea><br /><textarea rows="30" cols="150" id="tsv">%s</textarea></body>', s, tsv);
html += '<br /><input type="button" value="Exit" onClick="google.script.host.close();" />';
console.log(html);
var ui = HtmlService.createHtmlOutput(html).setWidth(800);
SpreadsheetApp.getUi().showModelessDialog(ui, 'Table Markdown');
}
HTML:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
select,
input {
margin: 2px 5px 2px 0;
font-size: 12px;
}
#cols {
margin: 2px 5px 2px 5px;
font-size: 12px;
}
.bold {
font-weight: "bold";
}
</style>
</head>
<body>
<form name="form">
<br /><input type="text" id="dtrg" name="datarange" placeholder="Select Data Range" size="20" readonly /><input type="button" value="Data" onClick="getDataRange();" title="Select Data Range." /><input type="text" id="cols" name="cols" size="2" readonly />Cols
<br /><input type="text" id="rdrg" name="redactrange" placeholder="Select Redact Ranges" size="20" readonly /><input type="button" value="Redact" onClick="getRedactRangelist();" title="Select Redact Rangelist." />
<br /><input type="text" id= "hdrs" name="headers" placeholder="Enter Column Header Separated by Comma" size="30" /><input type="button" id="autocol" value="auto" onClick="autoCols();" />
<br /><input type="text" id= "rows" name="rows" placeholder="Enter Row Numbers Separated by Comma" size="30" /><input type="button" id="autorow" value="auto" onClick="autoRows();" />
<br /><span class="bold">Alignment</span>
<br /><select name="align"><option value="l">left</option><option value="c">center</option><option value="r">right</option></select>
<input type="text" id="alntxt" name="aligntext" placeholder="Align all columns with r,c,or l only" size="25" oninput="getLength();" /><input type="text" name="col" id="col" size="2" readonly />
<br /><input type="text" id="rs" name="redactstring" size="15" />Redact String
<br /><input type="button" value="Submit" onClick="processForm(this.parentNode);" />
</form>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(obj){
if(obj.datarange) {$('#dtrg').val(obj.datarange);}
if(obj.redactrange) {$('#rdrg').val(obj.redactrange);}
if(obj.align) {$('#aln').val(obj.align);}
if(obj.redactstring) {$('#rs').val(obj.redactstring);}
if(obj.aligntext){$('$alntxt').val(obj.aligntext);}
})
.getTablePresets();
});
function getLength() {
let s=$('#alntxt').val();
let all='rlc';
if(!all.includes(s[s.length-1])){
$('#alntxt').val(s.slice(0,-1));
}
$('#col').val($('#alntxt').val().length);
}
function getDataRange() {
google.script.run
.withSuccessHandler(function(obj){
$('#dtrg').val(obj.datarange);
$('#cols').val(obj.columns);
})
.getCSVDataRange();
}
function getRedactRangelist() {
google.script.run
.withSuccessHandler(function(obj){
$('#rdrg').val(obj.redactrange);
})
.getRedactRangeList();
}
function processForm(form) {
google.script.run.redactableDataTable(form);
}
function autoCols() {
let s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
google.script.run
.withSuccessHandler((obj) => {
console.log(JSON.stringify(obj));
let hs = s.slice(obj.col - 1,obj.col + obj.width -1);
document.getElementById("hdrs").value = hs.split("").join(',');
})
.getUpperLeft();
}
function autoRows() {
google.script.run
.withSuccessHandler((obj) => {
let rs = obj.row;
document.getElementById("rows").value = Array.from(new Array(obj.height).keys(),x => x + rs).join(",");
})
.getUpperLeft();
}
console.log('My Code');
</script>
</body>
</html>

Related

Script for selecting sheets using control

I have in a spreadsheet, a MENU with buttons linked to other sheets.
Since I have many sheets, I want that when selecting a button it is the same for a group of those sheets.
Ex: btn "Enter approved teachers", there are 4 sheets that are to enter that data, each one corresponds to a different area.
That when I click on this button, I skip a "popup" that shows me a list of that sheets and lets me select it with an "OK".
The part of selecting the sheet with a script is easy to do with a youtube tutorial, but the control of sheets and selection I can not find anywhere.
I haven't been able to try anything, as I don't have experience coding in Apps Script or Js.
Hopefully this is close to what you are looking for:
This a dialog I use for selecting which files I wish to backup from my spreadsheets.
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
input {margin: 2px 5px 2px 0;}
#btn3,#btn4{display:none}
</style>
</head>
<body>
<form>
<input type="text" id="scr" name="script" size="60" placeholder="Enter Apps Script Id" onchange="getFileNames();" />
<br /><input type="text" id="fldr" name="folder" size="60" placeholder="Enter Backup Folder Id" />
<div id="shts"></div>
<br /><input type="button" value="0" onClick="unCheckAll();" size="15" id="btn3" />
<input type="button" value="1" onClick="checkAll();"size="15" id="btn4"/>
<br /><input type="checkbox" id="files" name="saveFiles" checked /><label for="files">Save Files</label>
<br /><input type="checkbox" id="json" name="saveJson" checked /><label for="json">Save JSON</label>
<br /><input type="button" value="Submit" onClick="backupFiles();" />
</form>
<script>
function getFileNames() {
const scriptid = document.getElementById("scr").value;
google.script.run
.withSuccessHandler((names) => {
document.getElementById('btn3').style.display = "inline";
document.getElementById('btn4').style.display = "inline";
names.forEach((name,i) => {
let br = document.createElement("br");
let cb = document.createElement("input");
cb.type = "checkbox";
cb.id = `cb${i}`;
cb.name = `cb${i}`;
cb.className = "cbx";
cb.value = `${name}`;
cb.checked = true;
let lbl = document.createElement("label");
lbl.htmlFor = `cb${i}`;
lbl.appendChild(document.createTextNode(`${name}`));
document.getElementById("shts").appendChild(cb);
document.getElementById("shts").appendChild(lbl);
document.getElementById("shts").appendChild(br);
});
})
.getAllFileNames({scriptId:scriptid});
}
function unCheckAll() {
let btns = document.getElementsByClassName("cbx");
console.log(btns.length);
for(let i =0;i<btns.length;i++) {
btns[i].checked = false;
}
}
function checkAll() {
let btns = document.getElementsByClassName("cbx");
console.log(btns.length)
for(let i = 0;i<btns.length;i++) {
btns[i].checked = true;
}
}
function backupFiles() {
console.log('backupFiles');
sObj = {};
sObj.script = document.getElementById('scr').value;
sObj.folder = document.getElementById('fldr').value;
sObj.saveJson = document.getElementById('json').checked?'on':'';
sObj.saveFiles = document.getElementById('files').checked?'on':'';
sObj.selected = [];
console.log("1");
const cbs = document.getElementsByClassName("cbx");
let selected = [];
for(let i = 0;i<cbs.length; i++) {
let cb = cbs[i];
if(cb.checked) {
sObj.selected.push(cb.value)
}
}
console.log("2");
google.script.run
.withSuccessHandler(function(obj){google.script.host.close();})
.scriptFilesBackup(sObj);
console.log(JSON.stringify(sObj));
}
</script>
</body>
</html>
This is what the dialog looks like:

Round robin assignment of tasks - AppScript solution?

I have a form response sheet (https://docs.google.com/spreadsheets/d/1YCneMRUC6ZKK0V3qs0mROhr6j62mdNIWAxcW71aAQIg/edit#gid=0) which saves all the requests from our stakeholders, and our workflow lead has to manually assign these requests to the members of our team in a round-robin fashion while ensuring each of the team members has an equal distribution of requests.
However, if a duplicate task is submitted (which is very much possible), it should be assigned to the same person that handled it earlier.
Is it possible to employ Google scripts solution to get this type of random yet equal distribution of tasks among the assignee group? The agent availability on any given day is also important, as they could be out of office, therefore the workflow lead keeps revising the agent list almost on a daily basis. Hence, it's all the more useful to have a Google AppScript solution to this problem (assigning one task at a time to the next available agent in queue). If the script can email the agent that would be ideal, but not necessary. Kindly advise! Thanks.
Round Robin Assignment
This script provides the following assignments:
If task title is repeated it assigns that task to original assignee.
If task title is new then it assigns that task to the assignee that has the least tasks.
If the title is new and all assignees have the same number of tasks then it makes a random selection with Math.floor(Math.random() * assigneeArray.length);
Here's the code:
Code.gs:
function onOpen() {
SpreadsheetApp.getUi().createMenu('My Tools')
.addItem('Add Task', 'addTask')
.addItem('Add Assignee', 'addAssignee')
.addSubMenu(SpreadsheetApp.getUi().createMenu('Utility')
.addItem('Select Columns Skip Header', 'jjeSUS1.selectColumnsSkipHeader')
.addItem('Create Named Range', 'jjeSUS1.createNamedRange'))
.addToUi();
}
function addAssignee() {
showFormDialog({filename:'addAssignee',title:'Add Assignee'});
}
function postAssigneeData() {
}
function addTask() {
showFormDialog({filename:'addTask',title:'Add Task'});
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function showFormDialog(dObj){
var form=HtmlService.createHtmlOutputFromFile(dObj.filename).getContent();
var ui=HtmlService.createTemplateFromFile('html').evaluate();
ui.append(form);
ui.append("</body></html>");
SpreadsheetApp.getUi().showModelessDialog(ui, dObj.title);
}
function saveData(dObj) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(dObj.sheetName);
var hrg=sh.getRange(1,1,1,sh.getLastColumn());
var hA=hrg.getValues()[0];
var vA=[];
for(var i=0;i<hA.length;i++) {
vA.push((dObj[hA[i]])?dObj[hA[i]]:'');//Column headers must agree with form names
}
dObj['row']=sh.getLastRow()+1;
var cA=Object.keys(dObj).filter(function(el){return (el!=='row' && el !='sheetName')});
for(var i=0;i<cA.length;i++) {
saveValue(dObj.row,cA[i],dObj[cA[i]],dObj.sheetName,1);
}
return dObj;
}
function makeAssignment(aObj) {
Logger.log(aObj);
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Assignments')
var title=aObj.Title;
var taskObj=getTasks();
//Check to see if someone has already done this once
if(taskObj.taskA.indexOf(title)>-1) {
saveValue(aObj.row,'Assignment',taskObj[aObj.Title],'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,taskObj[aObj.Title]);
}else{
var assA=getAssigneeTasks();
if(assA[0].allCountsEqual=='false') {
//they don't have the same number of tasks so take the lowest one
saveValue(aObj.row,'Assignment',assA[0].email,'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,assA[0].email);
}else{
//they all have the same number of task so take a random one
var n=Math.floor(Math.random()*assA.length);
saveValue(aObj.row,'Assignment',assA[n].email,'Assignments',1);
saveValue(aObj.row,'Date',Utilities.formatDate(new Date(),Session.getScriptTimeZone(), "E MM d, yyyy HH:mm"),'Assignments',1);
postTaskData(aObj.Title,assA[n].email);
}
}
return true;
}
function getTasks() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Data');
var taskObj={'taskA':[]};
var h=jjeSUS1.getColumnHeight(1, sh, ss);
if(h>2) {
var rg=sh.getRange(3,1,h-2,2);
var vA=rg.getValues();
for(var i=0;i<vA.length;i++) {
taskObj[vA[i][0]]=vA[i][1];
if(taskObj.taskA.indexOf(vA[i][0])==-1) {
taskObj.taskA.push(vA[i][0]);//Unique Task Array
}
}
}
return taskObj
}
function postTaskData(key,value) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Data');
sh.appendRow([key,value]);
}
function getAssigneeTasks() {
var taskObj=getTasks();
var aeqA=getAssignees().map(function(el){return {email:el,count:0,allCountsEqual:'false'}});
var keysA=Object.keys(taskObj).filter(function(el){return (el != 'taskA')});
for(var i=0;i<aeqA.length;i++) {
for(var j=0;j<keysA.length;j++) {
if(taskObj[keysA[j]]==aeqA[i].email){
aeqA[i].count+=1;
}
}
}
aeqA.sort(function(a,b){return a.count - b.count;});
var isTrue=true;
var maxCount=aeqA[aeqA.length-1].count;
aeqA.forEach(function(el){if(el.count!=maxCount){isTrue=false;}});
if(isTrue) {
aeqA.map(function(el){return el.allCountsEqual='true';});
}
return aeqA;
}
function saveValue(row,columnName,value,sheetName,headerRow) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(sheetName);
var hA=sh.getRange(headerRow,1,1,sh.getLastColumn()).getValues()[0];
sh.getRange(row,hA.indexOf(columnName)+1).setValue(value);
}
function getAssignees() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Assignees');
var hrg=sh.getRange(1,1,1,sh.getLastColumn());
var hA=hrg.getValues()[0];
return sh.getRange(2, hA.indexOf('Email')+1, sh.getLastRow()-1,1).getValues().map(function(r){return r[0]});
}
function closeDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('dummy');
SpreadsheetApp.getUi().showModelessDialog(userInterface,'Closing');
}
css.html:
<style>
body {background-color:#ffffff;}
input[type="button"],input[type="text"]{margin:0 0 2px 0;}
</style>
resources.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
html.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('resources') ?>
<?!= include('css') ?>
<?!= include('script') ?>
</head>
<body>
script.html:
<script>
$(function(){
document.getElementById('txt1').focus();
});
function getInputObject(obj) {
var rObj={};
var length=Object.keys(obj).length;
for(var i=0;i<length;i++){
console.log('Name: %s Type: %s',obj[i].name,obj[i].type);
if(obj[i].type=="text"){
rObj[obj[i].name]=obj[i].value;
}
if(obj[i].type=="select-one"){
rObj[obj[i].name]=obj[i].options[obj[i].selectedIndex].value;
}
if(obj[i].type="hidden"){
if(obj[i].name) {
rObj[obj[i].name]=obj[i].value;
}
}
}
return rObj;
}
function processForm(obj){
var fObj=getInputObject(obj);
console.log(JSON.stringify(fObj));
google.script.run
.withSuccessHandler(function(rObj){
document.getElementById("btn").disabled=true;
$('#msg').html('<br /><h1>Data Saved.</h1>');
if(rObj.sheetName=='Assignments') {
google.script.run
.withSuccessHandler(function(){
$('#msg').html('<br /><h1>Assignments Complete.</h1>');
google.script.host.close();
})
.makeAssignment(rObj);
}else{
google.script.host.close();
}
})
.saveData(fObj);
}
console.log('My Code');
</script>
addAssignee.html:
<div id="heading"><h1>Add Assignee</h1></div>
<div id="content">
<h3>Please Enter First Name, Last Name, Phone and Email into the text areas adjacent to the text box labels.</h3>
<form id="assigneeForm" onsubmit="event.preventDefault();processForm(this);" >
<br /><input type="text" id="txt1" name="First" /> First
<br /><input type="text" id="txt2" name="Last" /> Last
<br /><input type="text" id="txt3" name="Phone" /> Phone
<br /><input type="text" id="txt3" name="Email" /> Email
<br /><input type="hidden" value="Assignees" name="sheetName" />
<br /><input id="btn" type="submit" value="Submit" />
<br />
</form>
</div>
<div id="msg"></div>
<div id="cntl"><input type="button" id="btn" value="Close" onClick="google.script.host.close();" ></div>
addTask.html:
<div id="heading"><h1>Add Task</h1></div>
<div id="content">
<h3>Please Enter Title and Description into the text areas adjacent to the text box labels.</h3>
<form id="assigneeForm" onsubmit="event.preventDefault();processForm(this);" >
<br /><input type="text" id="txt1" name="Title" /> Title
<br /><input type="text" id="txt2" name="Description" /> Description
<br /><input type="hidden" value="Assignments" name="sheetName" />
<br /><input id="btn" type="submit" value="Submit" />
<br />
</form>
</div>
<div id="msg"></div>
<div id="cntl"><input type="button" id="btn" value="Close" onClick="google.script.host.close();" ></div>
The three pages of my spreadsheet look as follows: (names are on images)
JavaScript Arrays
JavaScript Objects
HtmlService
Templated Html
Public Libraries

Saving information in google sheets from my form

I try to save file on google drive and information in google sheets. But information in Sheets comes like Java.object.
Here is how it looks like in Code.gs :
var FOLDER_ID = '1jxBwrsz0JdBHcADpUUMespe';
var SHEET_ID = '1oJcKQ2RrtxxE1mn_CP-UAHefDxV7zg4';
function doPost(e) {
var data = Utilities.base64Decode(e.parameters.data);
var blob = Utilities.newBlob(data, e.parameters.mimetype, e.parameters.name);
var folder = DriveApp.getFolderById(FOLDER_ID);
var file = folder.createFile(blob);
folder.createFolder(name)
var res = [];
SpreadsheetApp.openById(SHEET_ID).getSheets()[0].appendRow([e.parameters.name, file.getUrl()].concat(res));
return ContentService.createTextOutput("Done.")
}
Here is how it looks like in the HTML file :
<form action="https://script.google.com/macros/s/AKfycbxkzg6ud1VyTI2W4gs-CRJRS3i3qLDXQIGevtyy/exec" id="form" method="post">
<div id="data"></div>
<div class="form-group">
<label for="name">Имя</label>
<input type="text" id='name' name="name" placeholder="Имя" />
</div>
<div class="form-group">
<label for="comment">Комм</label>
<input type="text" name="comment" placeholder="Комментарий" />
</div>
<input name="file" id="uploadfile" type="file">
<input id="submit" type="submit">
</form>
<script>
$('#uploadfile').on("change", function() {
var file = this.files[0];
var fr = new FileReader();
var name = $('#name').value;
fr.fileName = file.name
fr.onload = function(e) {
e.target.result
html = '<input type="hidden" name="data" value="' + e.target.result.replace(/^.*,/, '') + '" >';
html += '<input type="hidden" name="mimetype" value="' + e.target.result.match(/^.*(?=;)/)[0] + '" >';
html += '<input type="hidden" name="filename" value="' + e.target.fileName + '" >';
html += '<input type="hidden" name="name" value="' + name + '" >';
$("#data").empty().append(html);
}
fr.readAsDataURL(file);
});
</script>
Result in Google sheet
How to get data in a readable format?
Personally, I never use forms. Here's an examples that creates 5 text boxes. Validates that you put something into them and returns the code to the server via google.script.run for further processing (i.e. Logger.log the data). I create the html server side and loaded it on the on DOM ready event with JQuery.
Codes.gs
function buildForm(){
var s='';
for(var i=0;i<5;i++){
s+=Utilities.formatString('<br /><input class="jim" type="text" value="" id="%s" />%s',"txt" + i,"text" + Number(i+1));
}
s+='<br /><input type="button" value="Submit" onClick="getValues();" /><input type="button" value="Close" onClick="google.script.host.close();" />';
return s;
}
function saveValues(A){
for(var i=0;i<A.length;i++){
Logger.log('\nid=%s\nvalue=%s',A[i].id,A[i].value);
}
}
function showFormDialog(){
var ui=HtmlService.createHtmlOutputFromFile('form')
SpreadsheetApp.getUi().showModelessDialog(ui,'My Form');
}
form.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(html){$('#form').html(html);})
.buildForm();
});
function getValues(){
var elements=document.getElementsByClassName('jim');
var el=[];
console.log('elements.length=%s',elements.length);
for(var i=0;i<elements.length;i++){
var obj={};
obj['id']='txt' + i;
obj['value']=$('#' + 'txt' + i).val();
el.push(obj);
}
var dataValid=true;
for(var i=0;i<el.length;i++){
console.log('id=%s, value=%s',el[i].id, el[i].value);
if(!el[i].value){
dataValid=false;
$('#'+el[i].id).css('background','#ffff00');
}else{
$('#'+el[i].id).css('background','#ffffff');
}
}
if(dataValid){
google.script.run.saveValues(el);
}else{
alert('Invalid Data Found...Try again sucker....');
}
}
console.log('MyCode');
</script>
<style>
input {margin:5px 5px 0 0;}
</style>
</head>
<body>
<div id="form"></div>
</body>
</html>

Get data from HTML page to Google spreadsheet

I have four user input text fields in html created to user input data. I want to pass this four values into Google spreadsheet. This HTML is created using Google Apps Script.
I am not familiar with Google Apps Script but looking badly to develop a tool. Can anyone help me to work on this
This is a simple HTML file communicating with Google Apps Script contained in a Spreadsheet. The HTML file and the Google Apps Script communicate with each other and I pass one array from the HTML file to the Google Script.
The Code.gs file:
function doGet()
{
var html = HtmlService.createHtmlOutputFromFile('index');
return html.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
}
function getData(a)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "M/d/yyyy' 'HH:mm:ss");
a.splice(0,0,ts);
var ss=SpreadsheetApp.openById('SPREADSHEETID')
ss.getSheetByName('Form Responses 1').appendRow(a);
return true;
}
function getURL()
{
var ss=SpreadsheetApp.openById('SPREADSHEETID');
var sht=ss.getSheetByName('imgURLs');
var rng=sht.getDataRange();
var rngA=rng.getValues();
var urlA=[];
for(var i=1;i<rngA.length;i++)
{
urlA.push(rngA[i][0]);
}
return urlA;
}
The index.html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div id="data">
<br />Text 1<input name="t1" type="text" size="15" id="txt1" placeholder="Text 1" />
<br />Text 2<input name="t2" type="text" size="15" id="txt2" placeholder="Text 2" />
<br />Text 3<input name="t3" type="text" size="15" id="txt3" placeholder="Text 3" />
<br />Text 4<input name="t4" type="text" size="15" id="txt4" placeholder="Text 4" />
<br /><input type="radio" name="Type" value="Member" checked />Member
<br /><input type="radio" name="Type" value="Guest" />Guest
<br /><input type="radio" name="Type" value="Intruder" />Intruder
<br /><input type="button" value="submit" id="btn1" />
<br /><img id="img1" src="" alt="img1" width="300" />
</div>
<div id="resp" style="display:none;">
<h1>Response</h1>
<p>Your data has been received.</p>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
$('#btn1').click(validate);
$('#txt4').val('');
$('#txt3').val('');
$('#txt2').val('');
$('#txt1').val('')
google.script.run
.withSuccessHandler(setURL)
.getURL();
});
function setURL(url)
{
$('#img1').attr('src',url[0]);
}
function setResponse(a)
{
if(a)
{
$('#data').css('display','none');
$('#resp').css('display','block');
}
}
function validate()
{
var txt1 = document.getElementById('txt1').value || '';
var txt2 = document.getElementById('txt2').value || '';
var txt3 = document.getElementById('txt3').value || '';
var txt4 = document.getElementById('txt4').value || '';
var type = $('input[name="Type"]:checked').val();
var a = [txt1,txt2,txt3,txt4,type];
if(txt1 && txt2 && txt3 && txt4)
{
google.script.run
.withSuccessHandler(setResponse)
.getData(a);
return true;
}
else
{
alert('All fields must be completed.');
}
}
function loadTxt(from,to)
{
document.getElementById(to).value = document.getElementById(from).value;
}
function radioValue()
{
var radios = document.getElementsByName('genderS');
for (var i = 0, length = radios.length; i < length; i++)
{
if(radios[i].checked)
{
return radios[i].value;
}
}
}
console.log('My Code');
</script>
</body>
</html>

Validation from submitting in Apps Script

Please I need help. I have the same need of this post. I followed the instructions but I can't find my error. I'm frustratred.
When I submit with null fields, the script shows me a blank page.
When I submit with complete fields, the script shows me a blank page also and never upload the file.
This is my final code:
code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('form.html');
}
function uploadFiles(form) {
try {
var dropbox = "NHD Papers";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var blob = form.myFile;
var file = folder.createFile(blob);
file.setDescription("Uploaded by " + form.myName + ", Division: " + form.myDivision + ", School: " + form.mySchool + ", State: " + form.myState);
return "<h2>File uploaded successfully!</h2><p>Copy and paste the following URL into registration:<br /><br /><strong>" + file.getUrl() + '</strong></p>';
} catch (error) {
return error.toString();
}
}
form.html
<p>
<form id="myForm" onsubmit="validateForm();">
<h1>NHD Paper Upload</h1>
<label>Name</label>
<input type="text" name="myName" class="required" placeholder="Enter your full name..">
<label>Division</label>
<input type="text" name="myDivision" class="required" placeholder="(ex. Junior or Senior)">
<label>School</label>
<input type="text" name="mySchool" class="required" placeholder="Enter your school..">
<label>Affiliate</label>
<input type="text" name="myAffiliate" class="required" placeholder="Enter your affiliate..">
<label>Select file to upload. </label>
<input type="file" name="myFile">
<input type="submit" value="Submit File" >
<br />
</form>
</p>
<div id="output"></div>
<script>
function validateForm() {
var x=document.getElementsByClassName('required');
for(var i = 0; i <x.length; i++){
if (x[i].value == null || x[i].value == "")
{
alert("All fields must be filled out.");
return false;
}
this.value='Please be patient while your paper is uploading..';
var myFormObject = document.getElementById('myForm');
google.script.run.withSuccessHandler(fileUploaded)
.uploadFiles(myFormObject);
}
}
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}
</script>
<style>
input { display:block; margin: 15px; }
p {margin-left:20px;}
</style>
Regards,
In the IFRAME mode HTML forms are allowed to submit, but if the form has no action attribute it will submit to a blank page.
The solution suggested by the official documentation is to add the following JavaScript code to prevent all form submitions on load:
<script>
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
</script>
You can also add a return false; or event.preventDefault() at the end of your validateForm() function.