Good afternoon all;
I have a google spreadsheet which is populated by a Qualtrics form, where the user can upload a photo.
Using Apps Script, I have created a CRUD database that is accessed via a google sites page (html form). All is working great, but I'd like to display the photo that the user uploaded in their record; the URL to the image is saved in a column on the spreadhsheet.
The thing is, I'm not a very worthy 'coder' and I can't figure out how to make this happen.
I'm accessing my data like this: Note that I have removed a good portion of the code to save space.
Code.gs
function getFormValues(formObject) {
if (formObject.responseID && checkID(formObject.responseID)) {
var values = [[formObject.responseID.toString(),
formObject.permitNumber,
formObject.dateOfIssue,
formObject.photoLink, <<<this is where the URL would be pulled in. Column Q in the spreadsheet.`
DataTable.html
function populateForm(records){
document.getElementById('responseID').value = records[0][0];
document.getElementById('permitNumber').value = records[0][1];
document.getElementById('photoLink').value = records[0][16];
...I just don't know what to do after this point...
I did some tests, and I have an option for you to use Google Apps Script and the formula IMAGE.
Since I didn't have much information on your setup, like where the files are uploaded, which is the format of the URL, etc. I made a test environment using Google Forms, the files uploaded to Google Drive, and Apps Script.
With the formula:
=IMAGE("URL", [mode], [height], [width])
For the test, I use mode 4, which allows you to specify the size of the image. You can use either of the modes:
1 resizes the image to fit inside the cell, maintaining the aspect ratio.
2 stretches or compresses the image to fit inside the cell, ignoring the aspect ratio.
3 leaves the image at its original size, which may cause cropping.
4 allows the specification of a custom size.
The height and width needs to be in pixels.
When you use a Google Form to upload an image, you get an URL with the following format:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
There is a trick to display images from Google Drive into Google Sheets using the image formula. You need the URL with the format: (I got this idea from this post)
https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
So the script that I created modifies the URL in the column of the image from:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
To:
=IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]", 4, 60, 60)
And place it in the cell in the column. However, you can place it in the same column as the current URL too.
Here is the sample code:
function testImage() {
// Identifies the sheet link to the Google Sheet and the tab you will be working with
// you can also use const sheet = SpreadsheetApp.getActiveSpreadsheet()
// .getSheetId("[ID_OF_THE_SHEET]");
// if the Script is not bound to the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
// Gets the range and the values where the URLs of the images are located.
// in my example there are in column B
let range_1 = sheet.getRange("B2:B").getValues().flat();
// Filters the rows without values or empty
range_1 = range_1.filter((element) => {return (element !== '')});
// Start of the loop to run on each row with URLs
for (let i=0; i< range_1.length ; i++){
let row = range_1[i];
// For this sample, I replace the URL with the word "Done" to prevent duplicates later on in the Script
// This can be skipped or replaced place, in a different location.
if (row != "Done"){
// Replace the URL from https://drive.google.com/open?id=[ID_OF_THE_FILE]
// to =IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
let temp_imageURL = row.toString().replace('https://drive.google.com/open?id=',
'=IMAGE("https://lh3.googleusercontent.com/d/');
// However, the process is not complete yet, we need to complete the formula
// so we added the last part ",4,60,60) to the URL
// making it "https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]",4,60,60)
let imageURL = temp_imageURL+'",4,60,60)';
// place the complete formula in the current iteration, row 3 (which is row C)
sheet.getRange(i+2,3).setValue(imageURL);
// replace the current URL in Row 2 (which is row B) with the word "Done"
sheet.getRange(i+2,2).setValue("Done");
}
}
}
It looks like:
Reference:
IMAGE
Replace
Related
I programmed one that calls a Google Doc and in said Doc there is a variable called #Name# this is replaced by the name that is stored in a SpreadSheet.
My question is that to that Doc I want to add a variable called # Photo # and it is replaced by the photo stored in a Drive URL and this URL is in a SpreadSheet
How can I call that URL that is in Spreadsheet and be able to replace the field #photo#
With the name it does well with the function
body.replaceText ('#name#', name);
but with the image I don't know what to call it
Since #Photo# is a paragraph of its own, you can do the following:
Using the Document Body, find the text #Photo# via findText(searchPattern) and get the Element that corresponds to this text via getElement().
Once you have retrieved this element, you can remove the text (#Photo#), since the element itself will exist even if the text is an empty string. You can use setText("") for that.
Get the Blob from the image URL via UrlFetchApp.fetch(url) and getBlob().
Get the retrieved element's parent, which is the paragraph #Photo# was part of, and use insertInlineImage to insert the retrieved image to the beginning of this paragraph.
Code sample:
function replaceTextWithImage() {
var body = DocumentApp.getActiveDocument().getBody(); // Get current document's body
var element = body.findText("#Photo#").getElement(); // Get element of #Photo#
element.asText().setText(""); // Remove #Photo# placeholder
var url = "YOUR_IMAGE_URL"; // Change this accordingly
var blob = UrlFetchApp.fetch(url).getBlob(); // Get blob from image URL
var image = element.getParent().asParagraph().insertInlineImage(0, blob); // Insert image
}
Notes:
I'm assuming that your question was only about inserting the image to the Document, not about the previous steps of populating the spreadsheet, retrieving the image URL, etc.
This sample refers to a script that is bound to the Google Doc (hence, it's using getActiveDocument() to retrieve the Doc). If that's not the case, you should retrieve the document via openById(id) or openByUrl(url).
I'm very new using GAS what I'm trying to do is to copy some information that is already on the bottom of my doc to anywhere I want in my docs, this should work just by copying the information and paste it at the place I desire, but I want it to be done with Google App Script because it's a daily task and it's easier to do it with a function, instead of copying and pasting manually. Searching on how to do this, I found a lot of information about how to do it on Spreadsheets, but I needed it to be done on Google Docs. How can I do that?
If someone can guide me or send me a link to another similar question that would be very helpful, I don't know where to start.
This is what I have until now, I get all the data of the current doc and set it again to the page, the code gives me problems because it deletes my other information, also it selects all the doc's information. I want to select a piece of specific information and don't copy the content style.
function copyPasteInfo() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var notesText = body.getText();
body.appendPageBreak();
body.setText(notesText);
}
Link to the doc document
https://docs.google.com/document/d/1s2TCspXbjvHVurwhIWSdwJ_hMcZIoLTKj4FAB82nmhM/edit
Video example of how what i want to do
https://www.screencast.com/t/UmEon8Fm0lPe
Picture of the information i'm trying to copy and paste to the bottom of my doc
If I correctly understood your question, this code will help you to achieve your goal.
let body = DocumentApp.getActiveDocument().getBody();
function moveTable() {
// Get the last table and the previous table found in your Doc
const [previousTable, bottomTable] = getDesireTables();
// Make a copy of your last table
const bottomTableCopy = bottomTable.copy();
// Get the previous table's index
const previousTableIndex = body.getChildIndex(previousTable);
// Insert the last table's copy under the previous table in your Doc
body.insertTable(previousTableIndex + 1, bottomTableCopy);
// Remove the original last table
body.removeChild(bottomTable);
}
function getDesireTables(){
const tablesArr = body.getTables().slice(-3);
// Get the parent element type to check if it's a cell
const parentELementType = tablesArr[tablesArr.length - 1].getParent().getType();
if(parentELementType === DocumentApp.ElementType.TABLE_CELL){
// If there's a table inside a table, return this
return tablesArr.slice(0, 2);
}
else{
return tablesArr.slice(-2);
}
}
What I did was to get the last two tables in the Doc, then I made a copy of the last one and with the index of the previous one, I inserted it under the previous one.
Edit
I noticed you had a table inside a table. therefore I added the getDesireTables function. Which it will check if your bottom table has a table inside.
Docs
These are the docs I used to help you:
getTables().
copy().
insertTable(childIndex, table).
I've writen this function (thanks, #Mogsdad) to merge cells in a table in a text google document, like this:
function onOpen() {
// Add a menu with some items, some separators, and a sub-menu.
DocumentApp.getUi().createMenu('Sample')
.addItem('merge cells of a table', 'mergeCells')
.addToUi();
}
function mergeCells() {
var body = DocumentApp.getActiveDocument().getBody();
for (var p= 0; p< body.getNumChildren(); p++) {
var child = body.getChild(p);
if (child.getType() == DocumentApp.ElementType.TABLE){
// Assume we've already located our table
var table = child;
var tableRow = table.getChild(2); // gets third row
var tableCell = tableRow.getChild(1); // gets second cell in row
tableCell.merge(); // Merges seconde cell with first cell.
}
}
}
But when I run the code, I got this weird result (very different of the expected, with the merged cell with the same dimensions of the table):
Is there a way to fix it? (merged cell with the same dimensions)
[edit to address updates in Google Docs]
This currently not possible. You can know if a cell is merged by looking calling getColSpan and getRowSpan but there are no setter methods.
Please star the following issue to be notified by updates regarding this.
The merge function you found is not specific to table cells, it is there to merge any element with a previous sibling of the same type, joining their content.
[original answer]
If you were expecting to have a merged cell that, like what you can do in a spreadsheet, that is not possible. Simply because that's not possible in Google Documents (at least not yet). Therefore the API cannot do this (it can only do things that are also possible manually).
This merge function is not specific to table cells as you probably imagined. It is working as designed.
You can do this by a workaround. Add a drawing and add a table in this drawing document. In this document te option 'merge cell's' is a possibility if you select 2 cells and press the right mouse button. See this youtube video for a tutorial
Use the Advanced Documents Service, batchUpdate and mergeTableCells.
function mergeCells() {
const documentId = 'DOCUMENT_ID';
const resource = {
requests: [
{
"mergeTableCells": {
"tableRange": {
"tableCellLocation": {
"tableStartLocation": {
"index": 2
},
"rowIndex": 0,
"columnIndex": 0
},
"rowSpan": 1,
"columnSpan": 2
}
}
}
]
}
Docs.Documents.batchUpdate(resource, documentId);
}
Resources
https://developers.google.com/apps-script/guides/services/advanced#enabling_advanced_services
MergeTableCellsRequest
One workaround is to embed tables within tables instead of merging cells. That way you can still programmatically add or remove cells/rows without ruining the tables. If you set the cell padding to 0, any stray paragraphs to 1pt font and remove any cell borders and you can achieve almost the same effect without any merged cells.
For example, I've got a cell with 4 columns, where I want the last column to be merged as as a single cell. I also want to be able to add or remove rows with Apps Script.
Table embedded within a table:
This way I can add or remove rows from the embedded table on the left and the right cell will remain "merged". You will have trouble getting the cell borders to line up, but if you can do without them you can still get it looking nice, like this:
And without borders:
Here is how to solve the merged cell in tables of a MS document when converting to a Google document: The idea is to go back to the MS word document and remove the merged cells and then copy and paste it or convert MS word to Google document. In this way we can get easy conversion of tables in MS documents to Google documents. But, within Google documents the cell can't be merged.
I'm new to the world of google sheets. I was able to program very simple routines in Excel. Sorry if I couldn't find the answer anywhere on this site.
My question is simple, has 2 parts:
I would like to prompt a question box on opening the google sheet, asking a numeric value, after answering this, storing the numeric value to a specific cell, e.g. B3.
I would like to have a button on multiple pages of the sheet, prompting the same question box to edit the value.
Background:
I'm a medical doctor, I've created a sheet with many medications for small children in emergency situations. I would like a ask the user the age (and maybe later the weight, it's now calculated) of the child, so the age and weight are correct on all pages and the user is not in a hurry to find the right cell. If the user would like to edit the age, he uses the button, available on all sheets.
First, with the following function you can ask the user for the age and set it to a specific cell in a specific sheet:
function promptUserAge()
{
// Prompt for the value
var age = SpreadsheetApp.getUi().prompt("Please enter the age.").getResponseText();
// Get the sheet that you want store the value in and set the value in the cell B3
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Patient Information").getRange("B3").setValue( age );
}
Now, since you used the phrase
so the age and weight are correct on all pages
I am not sure if you are going to set the value in multiple sheets, and are you going to do that with Sheets functions or app script. That is why I am going to include the method to set the value in all sheets, in specific cell:
function promptUserAge()
{
// Prompt for the value
var age = SpreadsheetApp.getUi().prompt("Please enter the age.").getResponseText();
//Get all pages in spreadsheet and iterate through
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++ )
{
//Set the value in the cell B3 of the page
sheets[i].getRange("B3").setValue( age );
}
}
To get the prompt to pop-up when user opens the Spreadsheet you use call it in the onOpen function:
function onOpen(e)
{
promptUserAge();
}
And now, the part where you add the button to edit the value.
You could add a custom menu that would appear in the Spreadsheet's toolbar with a custom menu like this :
function createMenu()
{
SpreadsheetApp.getUi().createMenu("Fill information")
.addItem("Age", "promptUserAge")
.addToUi();
}
This is a good way if your users know what to look for, meaning that you have told them the custom menu exists. From my experience, some people have trouble finding the menu even if you tell them it is there, so since your background suggests you want to create an easy and fast way I would use the second method:
You could insert an image to the Spreadsheet by navigating from it's toolbar: Insert -> Image. Insert an Image that says 'Push here to edit information' in a very clear way and right click the image. You get the borders to edit the image size and in it's upper-right corner appears three dots. You click the dots and then "Assign script...". To the prompt, insert the function name without the parenthesis. In this example you would insert:
promptUserAge
Now the image works as a button to call the function.
I hope this helped.
I have the following chart:
var ss = ... // get sheet
var chart = ss.newChart().asLineChart();
chart.setTitle("My Title");
chart.addRange(dataSheet.getRange(1, 1, ss.getLastRow(), 4));
chart.setColors(["#3c78d8", "#a61c00", "#38761d"]);
chart.setXAxisTitle("X Title");
chart.setYAxisTitle("Y Title");
chart.setPosition(1, 1, 0, 0);
chart.setCurveStyle(Charts.CurveStyle.SMOOTH);
ss.insertChart(chart.build());
This code will use the show the first row as part of the data, instead of using it to label the legend. As a side note, if I use asColumnChart instead of line chart, it does the right thing.
How can I tell a chart to use the first row as headers using Google Apps Script?
First off - your code is OK. I took a copy and made minor changes to it just to use a single sheet for illustration:
The problem is likely with your data, specifically the first row. Here's that same example, with some of the "Headers" changed from text into numbers. I believe it is behaving in the bad way you're describing.
One more time, but with the numbers in the header forced to be text. (In the spreadsheet, I input them as '4 and '5 - the leading single quote tells Spreadsheet to treat as text.)
Just take a look at the data types in your first row, and make sure that they are Text.