Separate script for multiple google sheets - javascript

I have google sheet. There are two sheet named "Subjects" and "Subjects_Classes"
I have written a script for sheet "Subjects" as follows:
const ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/.../edit?usp=sharing")
const sheet = ss.getSheetByName("Subjects")
function doGet(e){
let obj = {};
let data = sheet.getRange("A1:B1000").getValues();
obj.content = data;
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON)
}
I am processing the same with Javascript in html as follows: It is working perfectly.
fetch('https://script.google.com/macros/s/AKfycbxAlLPXi0Knsl1xEUQGpyUgZ26oeVtlLlajiHHGc9sMHMjkg9WKZigqUvbRiunkcA1UwQ/exec')
.then(res => res.json())
.then(data => {
let subjectsdiv = "";
data.content.forEach(item => {
if (item[0] === "") {
// skip to the next iteration if item[0] is empty
return;
} else {
subjectsdiv += `<div class="col">
<div class="card h-100">
<img src="#" class="card-img-top img-responsive" alt="...">
<div class="card-body">
<h5 class="card-title">${item[0]}</h5>
<p class="card-text">Explore the many ${item[0]} resources available online to enhance your understanding of the subject</p>
</div>
Read
</div>
</div>`;
}
});
document.querySelector("#Subjects").innerHTML = subjectsdiv;
});
</script>
Now I need another JSON for sheet "Subjects_Classes" as follows: Hence Sheet and columns are different.
const ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/ ... /edit?usp=sharing")
const sheet = ss.getSheetByName("Subjects_Classes")
function doGet(e){
let obj = {};
let data = sheet.getRange("A1:C1000").getValues();
obj.content = data;
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON)
}
I need help to create script for another sheet. Also need help to access the same in html (if changes is required).

Although I'm not sure whether I could correctly understand your expected situation, how about the following modification?
In this modification, the spreadsheet and range are changed using a query parameter.
Google Apps Script side:
Please set your Spreadsheet ID and range to object.
function doGet(e) {
const object = {
pattern1: { spreadsheetId: "###", range: `'Subjects'!A1:B1000` }, // Please set your Spreadsheet ID and range.
pattern2: { spreadsheetId: "###", range: `'Subjects_Classes'!A1:C1000` }, // Please set your Spreadsheet ID and range.
};
if (object[e.parameter.pattern]) {
const { spreadsheetId, range } = object[e.parameter.pattern];
let obj = {};
let data = SpreadsheetApp.openById(spreadsheetId).getRange(range).getValues();
obj.content = data;
return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON)
}
return ContentService.createTextOutput(JSON.stringify({content: []})).setMimeType(ContentService.MimeType.JSON)
}
Javascript side:
Please set your Web Apps URL including the query parameter like ?pattern=pattern1 or ?pattern=pattern2 like https://script.google.com/macros/s/###/exec?pattern=pattern1.
From:
fetch('https://script.google.com/macros/s/AKfycbxAlLPXi0Knsl1xEUQGpyUgZ26oeVtlLlajiHHGc9sMHMjkg9WKZigqUvbRiunkcA1UwQ/exec')
To:
fetch('https://script.google.com/macros/s/AKfycbxAlLPXi0Knsl1xEUQGpyUgZ26oeVtlLlajiHHGc9sMHMjkg9WKZigqUvbRiunkcA1UwQ/exec?pattern=pattern1')
or
fetch('https://script.google.com/macros/s/AKfycbxAlLPXi0Knsl1xEUQGpyUgZ26oeVtlLlajiHHGc9sMHMjkg9WKZigqUvbRiunkcA1UwQ/exec?pattern=pattern2')
You can retrieve the values from 'Subjects'!A1:B1000 and 'Subjects_Classes'!A1:C1000 by the query parameter.
Note:
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".

Related

How to clear certain cells on opening a spreadsheet

I am trying to get my Google Sheet to clear certain cells on opening it. I've never added Apps Script to any sheet, so step by sp instructions will be greatly appreciated.
I'm getting an error when trying to deploy the script below.
function onOpen() {
function clearRange() {
// replace 'Sheet2' with your actual sheet name
// replace '1AsVArsUf5DaIXqzyPEokCRkPVglxSPW4NgWg_PVtLhA/edit#gid=1395849459' with your actual sheet ID
var sheetActive = SpreadsheetApp.openById("1AsVArsUf5DaIXqzyPEokCRkPVglxSPW4NgWg_PVtLhA/edit#gid=1395849459").getSheetByName("Sheet2");
sheetActive.getRange('G83,G143,E143,G210,G221').clearContent();
}
}
Clear Ranges:
function onOpen() {
clearRange()
}
function clearRange() {
const ss = SpreadsheetApp.openById("1AsVArsUf5DaIXqzyPEokCRkPVglxSPW4NgWg_PVtLhA/edit#gid=1395849459");
const sh = ss.getSheetByName("Sheet2");
const rgl = sh.getRangeList(['G83','G143','E143','G210','G221']).getRanges().forEach(r => r.clearContent())
}
You probably need an installable trigger because you are using openById()
Sheet.getRangeList

Cant work out out fetch api of met office to html page

Been tasked with displaying details from the met office data point api using javascript onto a html page and have only a brief knowledge of using the fetch api function.The met office uses users keys for their data point that needs inserted into the url in order to access the data. Im currently using the url that displays location information and would like to start with displaying the longitude and lattidide.
Ive wrote a html page and used an example I found of using fetch online but still no luck of displaying an data.
async function getLocations() {
let url = 'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/sitelist?key=231f9773-2c4f-4afd-beee-6878c1c63f0a';
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
async function renderLocations() {
let locations = await getLocations();
let html = '';
locations.forEach(Location => {
let htmlSegment = `<div class="Location">
<h2>${Location.longitude} ${Location.latitude}</h2>
</div>`;
html += htmlSegment;
});
let container = document.querySelector('.container');
container.innerHTML = html;
}
renderLocations();
<div class="container"></div>
By trying out the API endpoint by myself and logging out the result of getLocations(), I see that the structure of the Object looks like:
{
Locations: {
Location: [
...data
]
}
}
So to fix your code, change from
locations.forEach(Location => {
to
locations.Locations.Location.forEach(Location => {

How do I get Snipe-IT API to retrieve data using a Google App Script and populate it on a Google Sheet?

I am attempting to create a visualization of the data we have on Snipe-IT Asset Management on Google Data Studio. To do so, I am creating a Google Sheets spreadsheet with an App Script extension that will communicate with the Snipe-IT API, retrieve the data, and populate it on the Google Sheet. So far, I've been able to get the API to populate some of our data on the terminal but not on the spreadsheet itself.
I wrote a simple script that should list out the assets assigned to one particular user, for testing purposes. Again, the script works fine, it populates the data I need on the terminal but not on the Google Sheet. Here is my exact code (excluding SETUP section details):
//SETUP
serverURL = 'SERVER-URL'; (ignore)
apiKey = 'API-KEY' (ignore)
function onOpen(e) {
createCommandsMenu();
}
function createCommandsMenu() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run Script')
.addItem('Get Assets By Department', 'runGetAssetsByDepartment')
.addToUi();
}
function testGetAssetsByUser(){
getAssetsByUser("1745")
}
//Get assets for a user by id
//Returns a list of assets by id
function getAssetsByUser(userID) {
var url = serverURL + 'api/v1/users/' + userID + '/assets';
var headers = {
"Authorization" : "Bearer " + apiKey
};
var options = {
"method" : "GET",
"contentType" : "application/json",
"headers" : headers
};
var response = JSON.parse(UrlFetchApp.fetch(url, options));
var rows = response.rows;
var assets = []
for (var i=0; i<rows.length; i++) {
var row = rows[i];
if (row.category.name == "Laptop" || row.category.name == "Desktop" || row.category.name == "2-in-1") {
var asset = row.id
assets.push(asset)
}
}
return assets
}
console.log(getAssetsByUser(1745));
There are several ways to write values into an spreadsheet
The basics
Grab the spreadsheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
This works in bounded projects and add-ons
Grab the destination sheet
var sheet = spreadsheet.getSheetByName('put here the sheet name');
You might use SpreadsheetApp.getActiveSheet() if your spreadsheet has only one sheet, but for functions being called from a custom menu this one is risky.
Write values into the destination sheet
Preparation: Make a 2D Array
var output = assets.map(v => [v]);
Do: Write values into the destination sheet
sheet.getRange(1,1,output.length, 1).setValues(output);
Resources
https://developers.google.com/apps-script/guides/sheets

Extend seach range for all sheets

I am using this script and an input form to search a Google spreadsheet, this script has a range of one sheet,i.e. Data , but I need to extend my range to all sheets in the same spreadsheet, any ideas?
function doGet(e) {
return HtmlService.createTemplateFromFile("Index").evaluate()
.setTitle("WebApp: Search By Password")
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
/* PROCESS FORM */
function processForm(formObject) {
var concat = formObject.searchtext + formObject.searchtext2;
var result = "";
if (concat) {//Execute if form passes search text
result = search(concat);
}
return result;
}
//SEARCH FOR MATCHED CONTENTS ;
function search(searchtext) {
var spreadsheetId = ' '; //** CHANGE !!!!
var sheetName = "Data"
var range = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName).getDataRange();
var data = range.getValues();
var ar = [];
data.forEach(function (f) {
if (~[f[8]].indexOf(searchtext)) {
ar.push([f[2], f[3], f[4], f[5], f[6], f[7]]);
}
});
return ar;
}
I believe your goal is as follows.
You want to search for a text from all sheets of a Google Spreadsheet.
You want to achieve this by modifying the script in your question.
In this case, I would like to propose modifying the function search as follows.
Modified script:
function search(searchtext) {
return SpreadsheetApp
.getActiveSpreadsheet()
.getSheets()
.flatMap(s =>
s
.getDataRange()
.getValues()
.filter(r => r[8] && r[8].includes(searchtext))
.map(([,,c,d,e,f,g,h]) => [c,d,e,f,g,h])
);
}
Note:
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
References:
getSheets()
filter()
map()

How to iterate on the links to google forms in the cells of a spreadsheet column to get their questions?

I have a Google spreadsheet with links to questionnaires. I wanted to know how to get the questions from each of the questionnaires.
I guess I have to do: at best, use the script editor and iterate on the lines, and at worst, do webscraping.
const puppeteer = require('puppeteer');
function appendString() {
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var numRows = range.getNumRows();
var numCols = 0;
for (var i = 1; i <= numRows; i++) {
for (var j = 1; j <= numCols; j++) {
var currentValue = range.getCell(i,j).getValue();
await page.goto(currentValue);
const pollFrame = page.frames().find() # From there I have some difficulties
}
}
}
But I get the following error:
SyntaxError: await is only valid in async function (ligne 10, fichier "Code.gs")
Not to mention the async problem or the buttonthat I still have to click, the selection looks like this:
<div class="freebirdFormviewerViewItemsItemItemTitle exportItemTitle freebirdCustomFont" id="i1" role="heading" aria-level="3" aria-describedby="i.desc.310938276">How often did you fly before the Covid-19 epidemic? </div>
But the IDs don't follow a logical numerical order, so I don't know how to extract them automatically.
Then I don't know how to do it. I wonder if it's simpler because they're products from the same supplier.
Here is the equivalent in csv format:
https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link
Update
So I tried the anwer kindly posted by Neven Subotic's:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
})
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Unfortunately I obtain the following error message with the following details Exception: No item with the given ID could be found, or you do not have permission to access it. (line 9, "const form = FormApp.openById( formId );"). I don't understand. As you can see in the gif, I can open these links, so I should have the permission to access them isn't it?
I also tried Ruben's ideas with:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link"]//,
//"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
//"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
function scrapeForms(){
formIds.forEach( formId => {
// The code below logs the HTML code of the Google home page.
var response = UrlFetchApp.fetch(formId);
results = response.getElementsByClassName("freebirdFormviewerViewItemsItemItemTitleContainer");
Logger.log(results.getContentText())
});
}
But got back:
TypeError: response.getElementsByClassName is not a function (ligne 13, fichier "Code")
According to What is this Javascript "require"? require is not part of the standard JavaScript an AFAIK it's not supported by Google Apps Script.
By the other hand, the error message can't be easily solved as Google Apps Script Chrome V8 engine doesn't support async functions. Related Is google apps script synchronous?
If you will be using Google Apps Script, and you are the form owner or a form editor, instead of trying to web scraping a Google Form use the Forms Service of Google Apps Script. For this you will need the form ../edit URLs instead of the ../viewform URLs. On the official docs there is a quickstart that might help you https://developers.google.com/apps-script/quickstart/forms.
You could use openByUrl to "open" a form. It will not be actually opened in your web browser, it will be opened on the server side. Then you could use getItems to get all the questions, sections, images, videos, etc.
If you aren't the form owner or a form editor then you should use UrlFetchApp service and somehow parse the web page source code of each form based on the position of the questions. Related question: Google Sheets: How to import the following data?
Also, if the form has several sections you should do a post request to emulate clicking on the next button in order to get the second and following sections. There are more "also if the form has..." but I will stop here as the main part of question was already answered, I think.
You first want to get all the forms, so place those in an array:
const formIds = ["someId", "anotherId", "andSoOn"]
Then, lets use the FormApp to get the form and all items. Items can be of different types, see documentation.
// this array will store forms and their questions
let formAndQuestions = [];
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
}
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Then you can initially log out the results and see what it looks like:
Logger.log( formAndQuestions )
Item Types

Categories