Grouping Data in Google Apps Script Charts - charts

I'm trying to build a simple dashboard app using google apps script to pull data from a google spreadsheet and then display that data visually using charts on the dashboard.
I've managed to carry out those initial steps but am having problems trying to group the data to prevent multiple slices in the pie chart being assigned to the same task. In the below source spreadsheet you'll see numerous entries of the "Support" task (column E) linked to different Clients (column B). What I need is to group the data based on the "Task" column (E) so the time entries recorded in column G (Hours) are totaled and displayed accordingly (i.e. one segment of the pie chart for each task regardless of client).
I've searched the Apps Script Documentation and have essentially got lost in the noise and can't find any definitive way of doing this directly in Apps Script. I've found some info about potentially using Google Visualization to achieve something similar but am struggling with how to implement as there is so much conflicting documentation out there.
Essentially I'm asking is this possible within Apps Script directly without 1st Querying the Data and producing another sheet (as a basis for the chart) or if not then what the most straightforward and/or best approach would be?
Data Source:
https://docs.google.com/spreadsheets/d/1Gvqe89ytJxrKWOvGJWX5heYwrj5L-MP_Pe2jpTqqB5o/edit?usp=sharing
Current App displays the below:
Apps Script Code:
function doGet() {
var ss = SpreadsheetApp.openById('1Gvqe89ytJxrKWOvGJWX5heYwrj5L-MP_Pe2jpTqqB5o');
var data = ss.getDataRange();
var taskFilter = Charts.newCategoryFilter().setFilterColumnIndex(4).build();
var clientFilter = Charts.newStringFilter().setFilterColumnIndex(1).build();
var tableChart = Charts.newTableChart()
.setDataViewDefinition(Charts.newDataViewDefinition().setColumns([1,2,4,6]))
.build();
var pieChart = Charts.newPieChart()
.setDataViewDefinition(Charts.newDataViewDefinition().setColumns([4,6]))
.build();
var dashboard = Charts.newDashboardPanel().setDataTable(data)
.bind([taskFilter, clientFilter], [tableChart, pieChart])
.build();
var app = UiApp.createApplication();
var filterPanel = app.createVerticalPanel();
var chartPanel = app.createHorizontalPanel();
filterPanel.add(taskFilter).add(clientFilter).setSpacing(10);
chartPanel.add(tableChart).add(pieChart).setSpacing(10);
dashboard.add(app.createVerticalPanel().add(filterPanel).add(chartPanel));
app.add(dashboard);
return app;
}

Related

Need to show a card widget and after some delay automatically show another card widget regarding google workspace add-on creation

I need to show homeCard() and after I need to show settingsCard() automatically. Since I coudn't find a right method in app-script documentation I need some help for do this task.
Here I provided the code
function nevigateToUserSelectionPage(e) {
var navigation = CardService.newNavigation();
var builder = CardService.newActionResponseBuilder();
var userSelectionCardNavigation = navigation.pushCard(settingsCard());
return builder.setNavigation(userSelectionCardNavigation).build();
}
function homeCard() {
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
let participantsText = CardService.newTextParagraph()
.setText("<u>Home card here</u>");
let blink = CardService
.newImage()
.setImageUrl('https://res.cloudinary.com/deez2bddk/image/upload/v1646709349/icons8-dots-loading_x9q7jv.gif');
section.addWidget(blink);
section.addWidget(participantsText);
section.addWidget(AddSplah);
builder.addSection(section);
console.log('home card triggered!!!');
return builder.build();
}
function settingsCard() {
//const myTimeout = setTimeout(5000);
Utilities.sleep(10000);
builder = CardService.newCardBuilder();
section = CardService.newCardSection();
console.log('Settings card triggered!!!');
let participantsText = CardService.newTextParagraph()
.setText("<u>This is Settings Page....</u>");
section.addWidget(participantsText);
section.addWidget(getAuthenticationStepperImage());
builder.addSection(section);
return builder.build();
}
in code.gs file
function mainController() {
return homeCard();
}
Above code blocks I need to execute homeCard() function and then , settingsCard() but I can`t find a proper solution in workspace add-on creation documentation provided by google.
After doing some research, I think the CardService does not provide a method for non-interactive updates.
You can update the view based on user click interaction, as you can see in the Cats Quickstart. When the user clicks the cat image changes the image updates due the URL has a new parameter via new Date().getTime().
Apart from this, you have the triggers provided by Google, such as: homepageTrigger for common use case or onItemsSelectedTrigger specifically for Drive. You can review the full list here.
In summary: I think that what are you trying to achieve actually is not currently feasible within CardService.
If you wish Google adds some kind of time driven trigger to Google Workspace Add-ons, request it via this form.
Remember that in the actual state, HTML/CSS is not allowed, maybe this would be another possible path for your Feature Request.

Google Apps Script - Scripts in copied spreadsheet not retaining trigger

I am using a script in a Google Spreadsheet to, upon form submission, copy an existing spreadsheet and google form to create a new spreadsheet and new form and then connect the new form to the new spreadsheet so the new spreadsheet is receiving the responses from the new form.
The script in the copied spreadsheet is copied to the new spreadsheet, but the installed triggers don't exist. Is there a way to create those triggers from the original spreadsheet's script (the spreadsheet that received the form submission that created the new SS and form) or do I need to rely upon non-installed triggers in the new spreadsheet to create the installed trigger?
Triggers run scripts which require authorization by the user. Because your script is bound to a spreadsheet, it will need to be authorized on each copy.
I have a similar system (copies of a master sheet + code) and we addressed this by adding a custom menu to run a script when someone makes a copy. I added a custom menu and a setup script which would authorize the trigger.
function onOpen(e) {
var ui = SpreadsheetApp.getUi().createMenu("PGP Setup").addItem("Run", "setup").addToUi();
}
function setup() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('makeDocs')
.timeBased()
.everyHours(1)
.create();
}
It's easy on the user who created the sheet and has been reliable for us so far.
I was able to solve my issue with the code below. The function 'newSSTrigger' is in the original script (not a copied one) and is called after the new SS and Form are created and sync'd - this is where the the variable idOfNewSS comes from. The trigger will not create a script in the new objects or even be seen in the new object's scripts as an installed trigger. To find the trigger go to Edit>All Your Triggers from any script. Triggers not greyed out are attached to the document in some way.
This seems to have two benefits:
1) I never have to deal with any permissions - the triggers work without me touching the new docs at all.
2) If I update the 'myFunction' (below) it changes how the existing triggers work because the trigger retrieves its instructions from the original script - in its current state. This means I can update all existing triggers created by this script just by editing this function.
function newSSTrigger(idOfNewSS) {
var newSS = SpreadsheetApp.openById(idOfNewSS);
ScriptApp.newTrigger("myFunction")
.forSpreadsheet(newSS)
.onFormSubmit()
.create();
}
function myFunction() {
do stuff...
}
A different option that would work for people who are not just copying a sheet programmatically (or who are anticipating users making copies that the developer doesn't have permission to edit) is to take care of it for the user inside the bound script. You could use code similar to this:
function onOpen(){
var triggers = ScriptApp.getProjectTriggers();
if(triggers.length == 0){
ScriptApp.newTrigger('yourFunction')
.timeBased()
.everyHours(1)
.create();
}
This says that if there are no triggers, add a time-based trigger. You can see this documentation for variations.

How to read from excel and write into excel in Protractor?

I have integrated with Rally which downloads test cases .Every Test case has its own test data in excel spread sheet form.
I am planning to consolidate all test cases excel data into single excel sheet and read the test data from this consolidated excel as part of data driven testing.
So I would like to know how to read from excel and write into excel in protractor.
Hope i am clear .
Thank you.
You can use one of these node packages.
https://www.npmjs.com/package/xlsx
https://www.npmjs.com/package/edit-xlsx
I think the second one would be ideal for you as you need to edit existing excel files.
I'm using Exceljs to make my test cases data driven.
https://www.npmjs.com/package/exceljs
Code sample for reading from excel:
var Excel = require('exceljs');
var wrkbook = new Excel.Workbook();
wrkbook.xlsx.readFile('Auto.xlsx').then(function() {
var worksheet = wrkbook.getWorksheet('Master');
worksheet.eachRow(function (Row, rowNumber) {
console.log("Row " + rowNumber + " = " + JSON.stringify(Row.values));
});
});

SSRS ReportingService2010 change embedded DataSource to shared DataSource

I have SQL Server 2008 with SSRS installed on one server and SQL Server 2008 R2 with SSRS installed on a new server. I want to migrate 200+ reports as well as a few shared schedules and a couple data sources from the first server to the second one using the SSRS web service API. For simplicity sake since there are only a couple of shared data sources I went ahead and created those using the Report Manager interface.
Unfortunately, those who came before me embedded the data source information in each report (connection string, login, password, etc). I thought this would be a great time to change these to point to the shared data source so this would not have to be done for each report, one by one. I can create the reports on the new server just fine using CreateCatalogItem but I can't seem to determine how to properly go about changing from an embedded data source to a shared data source.
So far I have tried both SetItemReferences:
itemRef.Reference = "/Data Sources/TMS";
itemRef.Name = "TMS";
itemRef.Reference = "/Data Sources/TMS";
rs2010.SetItemReferences(catItem.Path, new ReportService2010.ItemReference[] { itemRef });
and SetItemDataSources:
ReportService2010.DataSourceReference dataSourceRef = new ReportService2010.DataSourceReference();
dataSourceRef.Reference = "/Data Sources/TMS";
ReportService2010.DataSource dataSource = new ReportService2010.DataSource();
dataSource.Name = "TMS";
dataSource.Item = dataSourceRef;
rs2010.SetItemDataSources(catItem.Path, new ReportService2010.DataSource[] { dataSource });
Both methods result in a "NotFoundException" when attempted on a report with an embedded data source but they both work just fine on reports that are already pointing to a shared data source.
Furthermore, I have searched all over Google as well as StackOverflow for a solution but have found nothing. Can anyone point me in the right direction here?
So I kept working with the SetItemReferences method and had a brilliant idea that ended up working. The final code I used is below:
List<ReportService2010.ItemReference> itemRefs = new List<ReportService2010.ItemReference>();
ReportService2010.DataSource[] itemDataSources = rs2010.GetItemDataSources(catItem.Path);
foreach (ReportService2010.DataSource itemDataSource in itemDataSources)
{
ReportService2010.ItemReference itemRef = new ReportService2010.ItemReference();
itemRef.Name = itemDataSource.Name;
itemRef.Reference = "/Data Sources/TMS";
itemRefs.Add(itemRef);
}
rs2010.SetItemReferences(catItem.Path, itemRefs.ToArray());
The problem was that I was not using the same DataSource name as what was found in the report .rdl file. I was able to determine what the name should be using the GetItemDataSources method. Since this method returns an array and may have more than one item in said array, I looped through it to create multiple ItemReferences if more than one existed though I doubt that happens very often if at all.

GXT (Ext-GWT) + Pagination + HTTP GET

I'm trying to populate a GXT Grid using data retrieved from an online API (for instance, going to www.example.com/documents returns a JSON array of documents). In addition, I need to paginate the result.
I've read all the various blogs and tutorials, but most of them populate the pagination proxy using something like TestData.GetDocuments(). However, I want to get that info using HTTP GET.
I've managed to populate a grid, but without pagination, using a RequestBuilder + proxy + reader + loader. But it seems as though the actual loading of the data is "put off" until some hidden stage deep inside the GXT code. Pagination requires that data from the start, so I'm not sure what to do.
Can someone provide a simple code example which does what I need?
Thank you.
I managed to get this going, here is what I did:
First I defined the proxy and loader for my data along with the paging toolbat:
private PagingModelMemoryProxy proxy;
private PagingLoader<PagingLoadResult<ModelData>> loader;
private PagingToolBar toolBar;
Next is the creation of each one, initializing with an empty ArrayList.
proxy = new PagingModelMemoryProxy(new ArrayList<EquipmentModel>());
loader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy);
loader.setRemoteSort(true);
toolBar = new PagingToolBar(100);
toolBar.bind(loader);
loader.load(0, 100);
Last, I have a set method in my view that gets called when the AJAX call is complete, but you could trigger it anywhere. Here is my entire set method, Equipment and EquipmentModel are my database and view models respectively.
public void setEquipmentData(List<Equipment> data)
{
Collections.sort(data);
// build a list of models to be loaded
List<EquipmentModel> models = new ArrayList<EquipmentModel>();
for (Equipment equipment : data)
{
EquipmentModel model = new EquipmentModel(equipment);
models.add(model);
}
// load the list of models into the proxy and reconfigure the grid to
// refresh display.
proxy.setData(models);
ListStore<EquipmentModel> equipmentStore = new ListStore<EquipmentModel>(loader);
equipmentGrid.reconfigure(equipmentStore, equipmentColumnModel);
loader.load(0, 100);
}
The key here for me was re-creating the store with the same loader, the column model was pre-created and gets reused.