Is there a way to change the name of a custom property for existing page view data in Application Insights?
You can change any properties on page view event before it left the browser with telemetry initializer:
Add this code immediately after the initialization snippet that you get from the portal.
...
window.appInsights = appInsights;
// Add telemetry initializer
appInsights.queue.push(function () {
appInsights.context.addTelemetryInitializer(function (envelope) {
var telemetryItem = envelope.data.baseData;
// To check the telemetry item’s type:
if (envelope.name === Microsoft.ApplicationInsights.Telemetry.PageView.envelopeType) {
// this statement removes url from all page view documents
telemetryItem.url = "URL CENSORED";
}
// To set custom properties:
telemetryItem.properties = telemetryItem.properties || {};
telemetryItem.properties["globalProperty"] = "boo";
// To set custom metrics:
telemetryItem.measurements = telemetryItem.measurements || {};
telemetryItem.measurements["globalMetric"] = 100;
});
});
// end of insertion
appInsights.trackPageView();
Also, you can rename columns at the query time in the Analytics query by aliasing them or copying them:
pageViews | summarize sum(itemCount) by NameB=tostring(customDimensions.NameA) | ...
pageViews | extend NameB = customDimensions.NameA | ...
Related
I have a List, bound to an entityset from mainService. Same view contains a filter field. Once user enters some filtering criteria, the read should happen. I am already reading the OData entityset, results are coming back. But I have no luck to let the table be bound to that result
The binding in XML View
<List
id="list"
width="auto"
class="sapFDynamicPageAlignContent"
items= "{/ItProjHeadSet}"
busyIndicatorDelay="{masterView>/delay}"
noDataText="{masterView>/noDataText}"
mode="{= ${device>/system/phone} ? 'None' : 'SingleSelectMaster'}"
growing="true"
growingScrollToLoad="true"
updateFinished=".onUpdateFinished"
selectionChange=".onSelectionChange">
Then, when the GO button of the smart filter bar is clicked, I am triggering the onSearch event as follows:
onSearchProjects : function (oEvent) {
var oMasterPage = this.getView().byId("masterPage");
var that = this;
var aTokens = this._oMultiInput.getTokens();
var aMultiFilters = aTokens.map(function (oToken) {
var oProperties = oToken.data("range");
return new Filter({
path: oProperties.keyField,
operator: FilterOperator[oProperties.operation],
value1: oProperties.value1,
value2: oProperties.value2
});
});
oMasterPage.setBusy(true);
//Filter
this.getOwnerComponent().getModel().read("/ItProjHeadSet", {
filters: aMultiFilters,
success: function (oData) {
var list = that.getView().byId("list");
var projectModel = new JSONModel(oData);
var oModel = that.getView().getModel("/ItProjHeadSet");
oModel.setData(projectModel);
oModel.updateBindings(true);
error: function (oData) {
MessageToast.show(that.getResourceBundle().getText("noProjectsFetched"));
}
});
oMasterPage.setBusy(false);
},
The problem then is that although I am receiving the corresponding successful results in the read, the setData seems that it is happening to a different model than the one bound to the list.
Am I doing the right model update in the Success read?
Regards,
Martin
Solved by my own, by getting the list binding then applying filters:
List.getBinding("items").filter(aMultiFilters, "Application");
then there was no need to getOwnerComponent and all that
My code uses Javascript window.open to link to another domain. The url of the target page is dynamically generated. I want to track the analytics across the domains but cannot use the standard "linker" automatic method of gtag,js which automatically adds the _ga parameter to the 'a' tag href attribute or action attribute for a 'form'. I need to be able to manually add the_ga attribute to the dynamically generated URL which is then used in the window.open.
I have tried creating a dummy 'a href' and then simulating a 'click' using dispatchEvent with a preventDefault in the event handler. This works for a real click event (the _ga parameter is appended to the href URL which I can then extract and use) but not for a simulated click event with dispatchEvent.
The client ID can be easily obtained using the old analytics.js implementation but I can't find how to do it using gtag.js.
To put it simply, using analytics.js the following code can be used to obtain the "linkerParm" field:
ga(function(tracker) {
var linkerParam = tracker.get('linkerParam');
});
What is the gtag.js equivalent?
Can anyone help?
OK, I’ve figured out how to do this and thought I’d share it here. This is working for me but I can’t guarantee that it’s the best way.
It appears that gtag.js also loads analytics.js automatically, however it does not do the ‘create’ to set up the tracking ID.
In this example I am assuming that the standard global objects are used, window.dataLayer for gtag.js and window.ga for analytics.js.
function getLinkerParam(){
// our global variables
window.glpReady = false; // set to true when glpLinkerParam is set
window.glpLinkerParam = null; // the linkerParam
// define variables will be used in this function
var analyticsTimeout = null;
var timeoutFired = false;
var timeoutPeriod = 1000;
// Code to initialse analytics.js if necessary
function initialiseAnalytics(){
if (!window.GoogleAnalyticsObject){
window.GoogleAnalyticsObject = 'ga';
window.ga = window.ga || function(){
(window.ga.q = window.ga.q || []).push(arguments);
};
window.ga.l = 1 * new Date();
}
}
// initialise analytics.js if necessary
checker: {
// is gtag.js installed?
if (window.dataLayer) { // Yes
// obtain the tracking ID from the dataLayer
for (var i = 0; i < window.dataLayer.length; i++){
if (window.dataLayer[i][0] == 'config') {
// initialise the tracking with a create
console.log('trackingID='+window.dataLayer[i][1]);
initialiseAnalytics();
window.ga('create', window.dataLayer[i][1]);
break checker;
}
}
}
else {
// gtag.js is NOT installed
// check for analytics.js installed
if (window.GoogleAnalyticsObject){
// analytics.js is installed in it;s own right
// so the create should already be done.
break checker;
}
}
console.log('analytics not installed or config tracker id not found');
window.glpReady = true;
return;
} // end checker
// here analytics.js is defined either in it's own right
// or as part of gtag.js. In either case the tracking ID should have been 'created'.
ga('require', 'linker');
ga(function(tracker) {
console.log('Client ID: '+tracker.get('clientId'));
});
// set a timeout for the linkerParam
analyticsTimeout = setTimeout(function(){
timeoutFired = true;
console.log('get linkerParam timeout fired');
window.glpReady = true;
return;
}, timeoutPeriod);
// get the linkerParam
console.log('Get linkerParam');
ga(function(tracker) {
var linkerParam = tracker.get('linkerParam');
console.log('got linkerParam: '+linkerParam);
clearTimeout(analyticsTimeout);
window.glpLinkerParam = linkerParam;
window.glpReady = true;
return;
});
}
I hope this helps someone.
I used a tutorial to help me sync my sheets to firebase with the use of a SYNC button that activates the script. The SYNC button currently sits just in the middle of the spreadsheet. I want to sync the data from sheets automatically to firebase when there are changes made.
function getFirebaseUrl(jsonPath) {
return (
'https://no-excusas.firebaseio.com/' +
jsonPath +
'.json?auth=' +
secret
)
}
function syncMasterSheet(sheetHeaders, sheetData) {
/*
We make a PUT (update) request,
and send a JSON payload
More info on the REST API here : https://firebase.google.com/docs/database/rest/start
*/
const outputData = [];
for(i = 0; i < sheetData.length; i++) {
var row = sheetData[i];
var newRow = {};
for(j = 0; j < row.length; j++) {
newRow[sheetHeaders[j]] = row[j];
}
outputData.push(newRow);
}
var options = {
method: 'put',
contentType: 'application/json',
payload: JSON.stringify(outputData)
}
var fireBaseUrl = getFirebaseUrl("UsersSheets")
UrlFetchApp.fetch(fireBaseUrl, options)
}
function startSync() {
//Get the currently active sheet
var sheet = SpreadsheetApp.getActiveSheet()
//Get the number of rows and columns which contain some content
var [rows, columns] = [sheet.getLastRow(), sheet.getLastColumn()]
// Get the data contained in those rows and columns as a 2 dimensional array.
// Get the headers in a separate array.
var headers = sheet.getRange(1, 1, 1, columns).getValues()[0]; // [0] to unwrap the
outer array
var data = sheet.getRange(2, 1, rows - 1, columns).getValues(); // skipping the header
row means we need to reduce rows by 1.
//Use the syncMasterSheet function defined before to push this data to the "masterSheet"
key in the firebase database
syncMasterSheet(headers, data)
}
Normally, it would be ok to just define an onEdit function in your code, like this:
function onEdit(event) {
startSync();
}
However, because you are making external requests via UrlFetchApp.fetch(), this will fail with an error about not having the https://www.googleapis.com/auth/script.external_request permission (gobs more detail about trigger authorization here).
Instead, you need to manually create an installable trigger
This is reasonably straightforward. In the edit menu for your code, go to your project's triggers:
Then, select "add a trigger" and create the on edit trigger, like so:
You should think about if you really want this running on every edit as the requests could be quite large (as it syncs the entire sheet) and run frequently (as you edit), however.
When you make a change to a spreadsheet, its onEdit event fires. So that's where you'd trigger that save with something like this:
function onEdit(event) {
startSync();
}
But since onEdit fires for each edit, this may end up saving a lot more than really necessary. So you may want to debounce to only save after some inactivity.
Something like this:
var timer;
function onEdit(event) {
// if we're counting down, stop the timer
if (timer) clearTimeout(timer);
// starting syncing after 2 seconds
timer = setTimeout(function() {
startSync();
}, 2000);
}
Using GoogleAppMaker how to create a data source from google contacts. There is an employee HR example app but I want to similarly manage contacts (add, modify, delete) and use select criteria.
At this time this task is not trivial in App Maker and it is pretty much generic. We can change question wording to CRUD operations with 3rd party datasources. Let's break it into smaller parts and address them separately.
Read/list contacts
This task is relatively easy. You need to use Calculated Model to proxy Apps Scripts Contacts API response. Once you create model with subset of fields from the Contact response you can create datasource for the model and bind it to List or Table widget. You can also try to find some inspiration in Calculated Model Sample.
// Server side script
function getContacts_() {
var contacts = ContactsApp.getContacts();
var records = contacts.map(function(contact) {
var record = app.models.Contact.newRecord();
record.FirstName = contact.getGivenName();
record.LastName = contact.getFamilyName();
var companies = contact.getCompanies();
if (companies.length > 0) {
var company = companies[0];
record.Organization = company.getCompanyName();
record.Title = company.getJobTitle();
}
var emails = contact.getEmails();
if (emails.length > 0) {
record.Email = emails[0].getAddress();
}
var phones = contact.getPhones();
if (phones.length > 0) {
record.Phone = phones[0].getPhoneNumber();
}
return record;
});
return records;
}
Create/Update/Delete
Since Calculated Models have some limitations, we need to turn on our imagination to create, update and delete records from their datasources. The basic strategy will be calling server side scripts for CUD operations in response to user actions on client side. To get user's input from UI we will need to utilize page's Custom Properties, one property for each Contact field:
Here are some snippets that should explain the idea
Create
// Client script
function onSubmitContactClick(submitButton) {
var props = submitButton.root.properties;
var contact = {
FirstName: props.FirstName,
LastName: props.LastName,
Organization: props.Organization,
...
};
google.script.run
.withSuccessHandler(function() {
// Most likely we'll need to navigate user back to the
// page with contacts list and reload its datasource
// to reflect recent changes, because our `CUD` operations
// are fully detached from the list datasource
app.showPage(app.pages.Contacts);
app.datasources.Contacts.load();
})
.withFailureHandler(function() {
// TODO: Handle error
})
.createContact(contact);
}
// Server script
function createContact(contactDraft) {
var contact = ContactsApp.createContact(contactDraft.FirsName,
contactDraft.LastName,
contactDraft.Email);
contact.addCompany(contactDraft.Organization, contactDraft.Title);
contact.addPhone(ContactsApp.Field.WORK_PHONE, contactDraft.Phone);
}
Update
Idea to update contact records will be very similar to the new contact creation flow, so I skip it for now.
Delete
Assuming that delete button is located inside contacts table row.
// Client script
function onDeleteContactClick(deleteButton) {
var email = deleteButton.datasource.item.Email;
google.script.run
.withSuccessHandler(function() {
// To update contacts list we can either reload the entire
// datasource or explicitly remove deleted item on the client.
// Second option will work way faster.
var contactIndex = deleteButton.parent.childIndex;
app.datasources.Contacts.items.splice(contactIndex, 1);
})
.withFailureHandler(function() {
// TODO: Handle error
})
.deleteContact(contact);
}
// Server script
function deleteContact(email) {
var contact = ContactsApp.getContact(email);
ContactsApp.deleteContact(contact);
}
I'm trying to write a script to grab Google Analytics data & add it to a Google Sheet.
When running the following code, I get the following error on the sheet:
"User does not have sufficient permissions for this profile."
Just a few quick check-box items:
Yes, I have admin permissions for the Analytics account I'm trying to access
Yes, I have admin permissions for the Google Sheet from which I'm creating the script
Yes, I've double-checked my current Google login to make sure I'm on the right account.
Here is the code:
function runDemo() {
try {
var results = getReportDataForProfile();
outputToSpreadsheet(results);
} catch(error) {
Browser.msgBox(error.message);
}
}
function getReportDataForProfile() {
var profileId = 'xxxxxxxx'; //firstProfile.getId();
var tableId = 'ga:' + profileId;
var startDate = getLastNdays(14); // 2 weeks (a fortnight) ago.
var endDate = getLastNdays(0); // Today.
var optArgs = {
'dimensions': 'ga:keyword', // Comma separated list of dimensions.
'sort': '-ga:sessions,ga:keyword', // Sort by sessions descending, then keyword.
'segment': 'dynamic::ga:isMobile==Yes', // Process only mobile traffic.
'filters': 'ga:source==google', // Display only google traffic.
'start-index': '1',
'max-results': '250' // Display the first 250 results.
};
// Make a request to the API.
var results = Analytics.Data.Ga.get(
tableId, // Table id (format ga:xxxxxx).
startDate, // Start-date (format yyyy-MM-dd).
endDate, // End-date (format yyyy-MM-dd).
'ga:sessions,ga:pageviews', // Comma seperated list of metrics.
optArgs);
if (results.getRows()) {
return results;
} else {
throw new Error('No views (profiles) found');
}
}
OK, I screwed around with this for a while and then it started working.
My best guess is I copied the property ID from Google Analytics wrong. After going back and recopying it, everything worked well.