Google Analytics API Multiple Queries - Second Function? - google-analytics

This is a rather newbie question, please bear with me...
I have successfully authorised my Google Analytics API account and am making the following call, which successfully prints the bounce rate.
I want to add more queries on for things such as pageviews and sessions, how do i do this?
It's the functions which are confusing me, do i need to create a new function for each query?
Thanks In Advance!
Tom
function getResults(&$analytics, $profileId) {
// Calls the Core Reporting API and queries for the number of sessions
// for the last seven days.
return $analytics->data_ga->get(
'ga:' . $profileId,
'today',
'today',
'ga:bouncerate');
}
function printResults(&$results) {
// Parses the response from the Core Reporting API and prints
// the profile name and total sessions.
if (count($results->getRows()) > 0) {
// Get the profile name.
$profileName = $results->getProfileInfo()->getProfileName();
// Get the entry for the first entry in the first row.
$rows = $results->getRows();
$sessions = $rows[0][0];
// Print the results.
print "<p>First view (profile) found: $profileName</p>";
print "<p>Total sessions: $sessions</p>";
} else {
print "<p>No results found.</p>";
}

The following part of your code is what actually makes the API request:
$analytics->data_ga->get(
'ga:' . $profileId,
'today',
'today',
'ga:bouncerate');
If you want to make additional queries, you'll have to call the $analytics->data_ga->get() method again. You don't need to write a new function for each query (if you don't want to), but you will need to invoke $analytics->data_ga->get() each time. How you choose to reuse that bit is up to you.

Related

I am noticing double entry (cpc and organic) for the same user ? little confused

I'm noticing double entry in google analytics. I have multiple ocurrences where it looks like the user came from the CPC campaign (which always has a 0s session duration) but that very same user also has an entry for "organic" and all the activities are logged under that.
My site is not ranked organically for those keywords. Unless a so many users come to my site, leave, and google for my "brand name" on google and revisits, this doesn't make sense.
I'm a little confused. Here's the report:
preview from google analytics dashboard
Based on the additional information in your comment, that the sites is a Single Page Application (SPA), you are most likely facing the problem of 'Rogue Referral'.
If this is the case, what happens, is that you overwrite the location field in the Analytics hit, losing the original UTM parameters, whereas referral is still sent with the hit, so Analytics recognizes the second hit as a new traffic source. One of the solutions is to store the original page URL and send it as the location, while sending the actual visited URL in the page field.
A very good article on this topic with further tips, by Simo Ahava, is available for your help.
Also please note, that as you have mentioned, that the first hit shows 0 second time on page, you might need to check, whether the first visited page is sent twice. E.g. sending a hit on the traditional page load event, and sending a hit for the same page as a virtual page view.
I have come up with a solution to this problem in a Gatsby website (a SPA), by writing the main logic in the gatsby-browser.js file, inside the onRouteUpdate function.
You can use this solution in other contexts, but please note that the code needs to run at the first load of the page and at every route change.
If you want the solution to work in browsers that do not support URLSearchParams I think you can easily find a polyfill.
Function to retrieve the parameters
// return the whole parameters only if at least one of the desired parameters exists
const retrieveParams = () => {
let storedParams;
if ('URLSearchParams' in window) {
// Browser supports URLSearchParams
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
const requestedParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'gclid'];
const hasRequestedParams = requestedParams.some((param) => {
// true if it exists
return !!params.get(param);
});
if (hasRequestedParams) {
storedParams = params;
}
}
return storedParams;
}
Create the full URL
// look at existing parameters (from previous page navigations) or retrieve new ones
const storedParams = window.storedParams || retrieveParams();
let storedParamsUrl;
if (storedParams) {
// update window value
window.storedParams = storedParams;
// create the url
const urlWithoutParams = document.location.protocol + '//' + document.location.hostname + document.location.pathname;
storedParamsUrl = `${urlWithoutParams}?${storedParams}`;
}
Send the value to analytics (using gtag)
// gtag
gtag('config', 'YOUR_GA_ID', {
// ... other parameters
page_location: storedParamsUrl ?? window.location.href
});
or
gtag('event', 'page_view', {
// ... other parameters
page_location: storedParamsUrl ?? window.location.href,
send_to: 'YOUR_GA_ID'
})

Cloud Functions for Firebase: onWrite() executed without new data

I use Cloud Functions for Firebase for some server-side-tasks. Using the database-trigger onWrite() I experience some unexpected behaviour.
exports.doStuff = functions.database.ref('/topic/{topicId}/new').onWrite((event) => {
// If data, then continue...
if (event.data.val()){
// doStuff
//
} else {
console.log("started, but no content!");
}
When new data is added to the specified folder the function is started at least once without any new content ("started, but no content!" is logged to the console). Sometimes even two, three or four times. Then it's run again, automatically (a couple of seconds later) and this time everything works as expected.
EDIT:
The code that writes to the specified node is as follows:
functionA(topicId){
return this.db.object('/topic/'+topicId+'/new').update({
timestamp: firebase.database.ServerValue.TIMESTAMP
});
}
The timestamp is only set once. So before that operation is called, the node new does not exist. So there is no edit or delete. However, this means, by calling the above function first the node new is created, some miliseconds later timestamp and then the value for timestamp. Does Firebase call the onWrite() function for each of these events?
Does this make sense to anybody? Any idea how to make sure, that the function is only executed, when there is really new data available?
onWrite() has a new format, since version 1.0 of Firebase SDK
Your original code was based on tutorials or help from an earlier, beta, version of Firebase.
exports.doStuff = functions.database.ref('/topic/{topicId}/new')
.onWrite((event) => {
if (event.data.val()){
// do stuff
}
}
However since version 1.0, the first parameter in the onWrite function is a "change" object which has two properties: before and after. You want the after. You can get it as follows:
exports.doStuff = functions.database.ref('/topic/{topicId}/new')
.onWrite((change) => {
if (change.after.val()){
// do stuff
}
}
Source: https://firebase.google.com/docs/reference/functions/functions.Change

Meteor/MongoDB pull data reactively

I have a method that checks for all unread messages belonging to a user. When the app loads, this number appears next to the "Messages" drop down. In Meteor, how would I update this count or variable for when a new message comes in or when the user reads an unread message? Pretty much I needs the method to send down the new count anytime a message status changes without refreshing the app itself.
I'm familiar with the Tracker.autorun functionality but I don't think it'll help with this situation. What's the best practice for approaching this?
Use Publish/Subscribe. It is always reactive. If you do not want to have all unread messages sent to the client straight away and counted there, you create a custom collection that justs count the number of unread messages and publishes that count. Look at the example a bit down in the linked page that starts with
// server: publish the current size of a collection
This is exactly your use case.
I have exactly this setup for new messages. In my header I have:
<li>Messages <span class="counter">{{Messages.count}}</span></li>
And then I have a helper that returns the cursor:
Template.header.helpers({
Messages: function(){ return Messages.find(); }
});
In the old days, before David Weldon set me straight I used to have a helper to return the count, now I just refer to the count directly in the blaze html template.
Now, in this approach I'm subscribing to the Messages collection so that new messages are transmitted to the client and can then be counted locally. This is on the assumption that they are going to be read soon. If you want to avoid this step then you should probably publish a Stats collection or include a stats key in the user object so that just the count itself can be synced via pub-sub.
You can just have a field like read, and update like:
Method for marking one message as read:
markRead: function(messageId){
Messages.update(messageId, {
$set: {
read: true //this needs to be set to false when its inserted
}
})
}
Bulk update method (assuming all messages have receiverId saved):
markAllRead: function(){
Messages.update({receiver: Meteor.userId(), read:false}, {
$set: {
read: true
}
}, {multi: true})
}
You can count read:false ones to retrieve count and you don't have to write anything else
Helper:
count: function(){
//even if your publish/subscribe is correct, the count we want is from messages that are not read and the receiver is current user.
return Messages.find({receiver: Meteor.userId(), read: false }).count();
}
Event:
'click .elementClass': function(){
//both users see the messages and they can both click. We want to update the right message for the right user. Otherwise, the other user can mark the message as read when the receiver is the other user which they shouldn't be able to do. You can do a simple check on the client side, and another check in the method if necessary.
if(this.receiver === Meteor.userId()){
Meteor.call('markAsRead', this._id)
}
}
Let me know if it solves your problem/answers all your questions.

Can Google Analytics email me if (crashes and) exceptions occur?

Google Analytics is correctly reporting exceptions thrown by my Android app. And I can use Scheduled Emails to send this report to me. However, receiving a daily email when there isn't anything to report (i.e., the report tells me that zero exceptions occurred) is tedious. Thus, I'd like to receive emails only when there is something to report (i.e., the report tells me that one or more exceptions occurred). It seems that Custom Alerts can be used for this purpose. However, Custom Alerts do not appear to be compatible with Exceptions. This leads me to my question.
Can Custom Alerts be configured to provide email notification on exceptions?
Or, more generally,
Can Google Analytics be configured to provide email notification on exceptions?
Also, does this work for crashes too?
UPDATE (22 Nov 2015, 1 Dec 2015)
(Partial) answer. I provide an answer that enables a server (not Google Analytics) to be configured to provide email notification on exceptions, which is probably a sufficient solution for many.
(Almost an) answer. jakub-kriz has provided a detailed answer, but it does not work as-is. Building upon the answer, I was able to configure Google Analytics to email when no exceptions occur. This is the exact opposite of what is required. Unfortunately, I have been unable to get emails when one or more exceptions occur.
Alternate direction. jakub-kriz has proposed an alternative solution, whereby normal events are used, rather than exception events. I haven't tried this direction.
A complete solution has not yet been proposed.
It is possible, but not in a direct way, you have to hookup you analytics quite dirty.
1) Configuration in Analytics Admin
Create two filters in Admin -> View -> Filters -> Custom -> Advanced
Create filter that listens on hitType exception and set Event Category - Exception
Create filter that replicates Exception description into Event Action
2) Create custom Goal
Create two filters in Admin -> View -> Goals -> Custom -> Event
Event Category equals Exception
3) Create Custom Alert
Custom alert by Goal containg exception
Do not forget your email
Try this and let me know!
To get report on Mail Id there is no way to send directly from google analytics. We can send this error report handling it and send it to programmatically to mail id from our app.
A server (not Google Analytics) can be configured to provide email notification on exceptions, which is probably a sufficient solution for many.
First, you need a service account, which can be created https://console.developers.google.com/project/_/apiui/credential. You'll create a key file (MyAnalytics.p12).
Secondly, we configure our analytics client (MyAnalytics.php):
<?php
//You'll need to install google-api-php-client
//(https://github.com/google/google-api-php-client)
require_once 'Google/autoload.php';
class MyAnalytics
{
//When logged into Google Analytics you'll have a URL that looks
//something like https://www.google.com/analytics/web/?authuser=0#home/a00w11p22/
//Your profile id is everything after the p
const PROFILE_ID = '22';
//This is the service account email that you constructed in step 1
const SERVICE_ACCOUNT_EMAIL = 'blah#developer.gserviceaccount.com';
//This is the file that you constructed in step 1.
const KEY_FILE_LOCATION = 'MyAnalytics.p12';
private $client;
private $analytics;
private $cred;
public function __construct() {
$this->client = new Google_Client();
$this->analytics = new Google_Service_Analytics($this->client);
$key = file_get_contents(self::KEY_FILE_LOCATION);
$this->cred = new Google_Auth_AssertionCredentials(
self::SERVICE_ACCOUNT_EMAIL,
array(Google_Service_Analytics::ANALYTICS_READONLY),
$key
);
}
public function getAnalytics() {
$this->client->setAssertionCredentials($this->cred);
if($this->client->getAuth()->isAccessTokenExpired()) {
$this->client->getAuth()->refreshTokenWithAssertion($this->cred);
}
return $this->analytics;
}
}
?>
Thirdly, we query and report on exceptions (exceptions.php):
<?php
require_once 'MyAnalytics.php';
$myAnalytics = new MyAnalytics();
$analytics = $myAnalytics->getAnalytics();
$results = $analytics->data_ga->get(
'ga:' . MyAnalytics::PROFILE_ID,
'yesterday',
'today',
'ga:exceptions'
);
$a = $results->getTotalsForAllResults();
$count = $a['ga:exceptions'];
echo $count;
if (is_numeric($count) && $count > 0) {
//handle the exception, e.g., send an email
//(cf. https://stackoverflow.com/a/5335311/3664487)
}
?>
Fourth, configure cron to run exceptions.php (cf. https://stackoverflow.com/a/22358929/3664487).

Subscribing to a reactive data source using autorun

I'm trying to write a webapp using Meteor and I'm definitely failing to grok something about subscribing to published datasets. The entire app is up on github (linked to the latest commit for posterity), but I'll try to summarize below.
I have a collection called teams which is available to both client and server:
Teams = new Meteor.Collection( "teams" );
On the server, I want to publish a list of all of the teams:
Meteor.publish( "allteams", function() { ...
There's a very simple cursor which makes up this published list:
var handle = Teams.find( {} ).observeChanges({
added: function( id ) {
console.log( "New team added" );
if ( !initializing ) {
console.log( "Telling subscribers it's all change" );
self.added( "teams", id, {} );
self.ready();
}
}
});
The client subscribes to that source, and when elements are added the client will add pins to a map:
Meteor.autorun( function() {
Meteor.subscribe( "allteams", function() {
console.log( "All teams has been updated" );
// Do more stuff
}
};
When the list is initially populated the autorun runs fine, but if I add another element to the collection then the publisher method logs to say "I noticed this" but nothing happens in the subscriber.
The aim of the above is as follows:
There is a list of teams on the server which consists of a name and long/lat details
When a client connects, they receive that list of teams and they're plotted on a map
If a team is added to the list on the server side, each client is notified and a new pin appears on the map.
In terms of the app, I probably don't need pins to magically appear, but it's a useful way of learning publish and subscribe, especially when I don't get it right! Eventually, "allteams" will probably be a bit more fine-grained than just the entire list of teams, so I guess it's akin to making a view on the data.
Am I missing something completely obvious?
Edit: I worked it out and put the answer below. tl;dr I wasn't subscribing to a reactive data source at all.
It's probably not polite to answer my own question, but I worked out what I was doing wrong and I figured that other people might come across the same thing.
Simple answer is that I wasn't doing what I claimed to be doing in the title of the queston, specifically, not subscribing to a reactive data source.
Meteor.autorun( function() {
Meteor.subscribe( "allteams", function() {
console.log( "All teams has been updated" );
// Do more stuff
}
};
Here I've passed the subscribe method to autorun, but that method itself isn't a reactive data source. However, it returns something which is!
// Define a subscription
var handle = Meteor.subscribe( "foo", { onReady: function() { ... } } );
Meteor.autorun( function() {
if ( handle.ready() ) {
// Now do something every time the subscription is marked as ready
}
};
The ready method of a subscription handle is reactive, so the autorun now executes every time the published document set is updated. That leads me to further questions about the efficiency of multiple clients subscribing to a database cursor and watching for changes, but I'll come to that in another question.
If autopublish is still on, this may be the cause of your problems. Try disabling autopublish and seeing if the publish statement now works properly.

Resources