How can I generate the status values(count) in Dashboard - google-app-maker

How can I generate the status values(count) in Dashboard (refer the image)
My status option is " WIP, "Open", Closed" and "Total",
I have tried generating the values using #datasources.Requset.query.filters.Status._equals but not succeed

I think you will need to create a Calculated Model first, in which you need to write your logic to count each status value, then you can display these values directly in this graph.
Here's my code to generate a pie chart on similar approach.
// server script
var calculatedModelRecords = [];
var recordsByStatus = {};
var allRecord = app.models.DataSource.newQuery().run();
var pendingrecord = app.models.NewCalculatedDatasource.newRecord();
pendingrecord.count = 0;
for (var i = 0; i < allRecord.length; i++) {
var record = allRecord[i];
if(record.Status == 'Pending') {
// follow same approach for rest of the status count
pendingrecord.count++;
}
}
calculatedModelRecords.push(record);
return calculatedModelRecords;

Related

Bug with Woocommerce Webhook and Google Sheet API?

I have set up a link between Woocommerce orders (a ecommerce plugin for WordPress that we use for our NGO), and a Google Sheet table using this script in Google Sheet's script editor:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var order_created = myData.date_created;
var order_status = myData.status;
var order_total = myData.total;
var billing_first_name = myData.billing.first_name;
var billing_last_name = myData.billing.last_name;
var billing_email = myData.billing.email;
var billing_phone = myData.billing.phone;
var shipping_first_name = myData.shipping.first_name;
var shipping_last_name = myData.shipping.last_name;
var shipping_address_1 = myData.shipping.address_1;
var shipping_address_2 = myData.shipping.address_2;
var shipping_postcode = myData.shipping.postcode;
var shipping_city = myData.shipping.city;
var shipping_country = myData.shipping.country;
var payment_method = myData.payment_method_title;
var currency = myData.currency;
var timestamp = new Date();
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
}
Everything works as intended, every new order is populated in the Google Sheet table a few seconds later.
However, when I apply a filter on any column in Google Sheet, let's say for payment method, selecting "PayPal", no new order will populate the Google Sheet's table.
They are registered in the woocommerce plugin, payment is ok, all is fine, except that Google Sheet does not receive the order.
Even after removing the filter, it doesn't appear.
All next orders will appear if all filters are deactivated in Google Sheet.
So, there is an issue with Google Sheet filters, but I don't know what is causing it. Is it my script? Is it Google API's fault? Woocommerce webhook?
Please note that I am not a developer, I found this script online and tweaked it myself by try and guess for my own needs.
Modification points:
When the sheet of Google Spreadsheet uses the basic filter, when the values are put using appendRow(), the values are not appended. This might be the current specification.
I thought that this might be the reason of your issue.
In your script, the values are put using appendRow(), and appendRow() is used in a loop. In this case, the process cost of the script will become a bit high. When setValues() is used, this issue can be also removed.
In this case, I would like to propose to append the values using setValues(). When setValues() is used, the values can be put to the filtered sheet. But, when the values are put to the filtered sheet, the filtered sheet is not changed while the values are put.
So it is required to refresh the basic filter after the values are put.
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify your script as follows.
From:
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
To:
var sheet = SpreadsheetApp.getActiveSheet();
var values = [];
for (var i = 0; i < myData.line_items.length; i++) {
var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
values.push([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]);
}
// Put values using "setValues".
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
// Refresh basic filter.
var filter = sheet.getFilter();
if (filter) {
var range = filter.getRange();
for (var i = range.getColumn(), maxCol = range.getLastColumn(); i <= maxCol; i++) {
var filterCriteria = filter.getColumnFilterCriteria(i)
if (filterCriteria) {
filter.setColumnFilterCriteria(i, filterCriteria);
}
}
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
setValues(values)
getLastRow()
getFilter()
Class Filter

Any way to find out the fields in a datasource?

I'd like to write a script to dump the contents of a given table to a spreadsheet regardless of the fields, (I've done this for several individual tables but want to write something universal).
Will
app.models.filesToCopy.fields._values;
be involved?
I would say the most generic way would be to establish a server function and pass in the model name as a parameter. Then establish a script similar to what I put below. You can even format your data based on the field type if wanted by using fields[n].type in a if or select statement.
function YourFunction(YourModel) {
var sheetfile = SpreadsheetApp.create('test file');
var ss = sheetfile.getActiveSheet();
var fields = app.metadata.models[YourModel].fields;
var results = app.models[YourModel].newQuery().run();
var i, j;
var sheetdata = [], header = [];
for (j in fields) {
header.push(fields[j].displayName);
}
sheetdata.push(header);
for (var i in results) {
var data = [];
for (j in fields) {
data.push(results[i][fields[j].name]);
}
sheetdata.push(data);
}
ss.getRange(1,1,sheetdata.length,fields.length).setValues(sheetdata);
}

Object overwritten in firebase

Data sending from google sheet:
Script using to send data to firebase from google sheet.
function writeData() {
var ss = SpreadsheetApp.openById("####");
var sheet = ss.getSheets()[0];
var data = sheet.getDataRange().getValues();
var dataToImport = {};
for(var i = 1; i < data.length; i++) {
var department = data[i][0];
var year = data[i][1];
var course = data[i][2];
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
dataToImport[department][year][course][i] = {
course: data[i][3],
dateAdded: data[i][4],
fileSize: data[i][5],
fileType:data[i][6],
downloadLink: data[i][7],
};
}
var firebaseUrl = "https:url";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
base.setData("",dataToImport);
}
Data that is sent:
Q: My questions that since i had parent electrical and its two childs that were 1 and 2 and each child had a course dsp and aes respectively but only 1 child is send that is the second one.
Why first child is not sent to firebase ?
This code replacing previous entry with new entry. Replace this -
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
with this -
dataToImport[department] = dataToImport[department] || {};
dataToImport[department][year] = dataToImport[department][year] || {}
dataToImport[department][year][course] = dataToImport[department][year][course] || {}
As a side note to #ra89fi's answer (because I didn't pick up on the overwrite bug).
The line base.setData("",dataToImport); is setting all data at the root of your database ("") with the given data (dataToImport). Any other data in your database will be deleted.
Instead, you should use an update operation.
I unfortunately can't establish which version of the Firebase API you are using. It's not quite the REST API and not quite JavaScript. So here are some relevant documentation links.
REST Update Guide
REST Update Reference
JavaScript (Web) Update Guide
JavaScript (Web) Update Reference

How to browse to the next page in a datasource that is loaded into table in Google AppMaker

I'm working on a requirement where I have a datasource named 'emailSearchResults' where I search for email messages metadata and load the results in the datasource.
The fields in the datasource are not relevant, however I set the datasource to have 50 records per page as per the below screenshot:
The script I used to load the datasource is shown in the query field, that call the following script:
function getMessageDetails(userId, msgID)
{
var messageDetails = [];
var messageData;
var msgID_,subject_,from_,date_;
messageData=Gmail.Users.Messages.get(userId,msgID,{format:"metadata", metadataHeaders:["Message-ID", "Subject", "From", "Date"]});
console.log(messageData.payload.headers);
//console.log(msgID);
//console.log(messageData.payload.headers[3].value);
date_="<na>";
from_="<na>";
subject_="<na>";
msgID_="<na>";
for (var counter =0;counter<4;counter++)
{
if (messageData.payload.headers[counter].name=="Message-ID")
{
msgID_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Subject")
{
subject_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="From")
{
from_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Date")
{
date_=messageData.payload.headers[counter].value;
}
}
messageDetails.push(date_);
messageDetails.push(from_);
messageDetails.push(subject_);
messageDetails.push(msgID_);
return messageDetails;
}
function searchMessages(userId,condition)
{
//
// first we build the conditions
// we can make it fixed
// or we can make it dynamic
var searchResult;
var deleteResult;
var currentMessage;
var results = [];
var pageToken;
var params = {};
var _stat;
var options = {
includeSpamTrash: "true",
pageToken: pageToken
};
var msgRecord = [];
do
{
searchResult=Gmail.Users.Messages.list(userId,options);
for (var i = 0; i < searchResult.messages.length; i++)
{
var record=app.models.emailSearchResults.newRecord();
msgRecord=getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID=searchResult.messages[i].id;
record.msgID=msgRecord[3];
record.subject=msgRecord[2];
record.senderAddress=msgRecord[1];
record.msgDate=msgRecord[0];
/*console.log(searchResult.messages[i].id);
console.log(msgRecord[3]);
console.log(msgRecord[2]);
console.log(msgRecord[1]);
console.log(msgRecord[0]);
return;*/
results.push(record);
msgRecord=null;
}
if (searchResult.nextPageToken) {
options.pageToken = searchResult.nextPageToken;
}
} while (searchResult.pageToken);
searchResult=null;
return results;
}
On the main page I put a table and linked it to the datasource, and I enabled pagination on the table, so I get the pager buttons at the bottom of the table as below:
When I execute the app and the datasource is filled, I see the first page results in a correct way, however when I want to move to the next page, I click the next page button and once the loading is complete I find out that I still see the same results from the first page on the table.
I am not familiar with how to make the table show the results of the second page then the third page, and I am going in circles on this...
Hope the explanation is clear and addresses the issue..
I would really appreciate any help on this!
Regards
Currently pagination isn't working as expected with calculated datasources. You can, however, build your own. There are several changes you'll need to make to accomplish this. First you'll want to refactor your searchMessages function to something like this:
function searchMessages(userId, pageToken){
var results = [];
var options = {
includeSpamTrash: "true",
pageToken: pageToken,
maxResults: 50
};
var searchResult = Gmail.Users.Messages.list(userId, options);
for (var i = 0; i < searchResult.messages.length; i++){
var record = app.models.emailSearchResults.newRecord();
var msgRecord = getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID = searchResult.messages[i].id;
record.msgID = msgRecord[3];
record.subject = msgRecord[2];
record.senderAddress = msgRecord[1];
record.msgDate = msgRecord[0];
results.push(record);
}
return {records: results, nextPageToken: searchResult.nextPageToken};
}
Then you'll want to change your datasource query. You'll need to add a number parameter called page.
var cache = CacheService.getUserCache();
var page = query.parameters.page || 1;
var pageToken;
if(page > 1){
pageToken = cache.get('pageToken' + page.toString());
}
var results = searchMessages('me', pageToken);
var nextPage = (page + 1).toString();
cache.put('pageToken' + nextPage, results.nextPageToken);
return results.records;
You'll need to modify the pagination widget's various attributes. Here are the previous/next click functions:
Previous:
widget.datasource.query.pageIndex--;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
Next:
widget.datasource.query.pageIndex++;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
You should be able to take it from there.

google places api many filters by type Ex.: restaurant,cafe

I'm trying to change the radius category/type filter for a checkbox, so I changed the var type to an array.
ORIGINAL WORKING:
var type;
for (var i = 0; i < document.controls.type.length; i++){
if (document.controls.type[i].checked){
type = document.controls.type[i].value;
}
}
startBox.setBounds(map.getBounds());
var search = {
// keyword: 'comocomo', // not needed with the autocomplete / startBox
bounds: map.getBounds()
};
if (type != 'establishment'){
search.types = [ type ];
}
places.search(search, function(placesArr, status){
THE ONE WITH THE ARRAY NOT WORKING: edited:
var type=[];
for (var i = 0; i < document.controls.type.length; i++){
if (document.controls.type[i].checked){
type.push(document.controls.type[i].value)
}
}
startBox.setBounds(map.getBounds());
var search = {
bounds: map.getBounds()
};
var quotedAndCommaSeparated = "'" + type.join("','") + "'";
alert(quotedAndCommaSeparated); // 'establishment','restaurant','lodging'
search.types = [ quotedAndCommaSeparated ];
I made many tests, and I don't see what I'm doing wrong. the map doesn't even show.
What's this meant to be, doesn't look like valid Javascript to me:
var type[];
Should be
var type = [];
Fix the javascript errors in your code otherwise the map won't show up.
Update:
What you have in quotedAndCommaSeparated is a string like "'a','b','c'" that looks a bit like the contents of an array: ['a','b','c']. But it's not an array, it's just a single string. If you check the length of your search.type array, I'm guessing it equals 1.
What you can do is split your string on the comma to turn it into an array:
search.types = quotedAndCommaSeparated.split(",");

Resources