1
votes

I'm creating a website-dashboard for internal use at my company. We've a TV-screen in our office and I want to display some stats of our 8 most popular websites. It should contain 8 graphs of last weeks users/sessions per site. I also want to display the active-users for each site.

I've managed to come up with a script and wrote (with some help) a function so I could easily call the different sites by just changing the view id's of the corresponding properties, instead of copying the same code eight times.

The problem I'm facing is a 'Quota Error: User Rate Limit Exceeded.' error in the console, because of the 8 sites. It's working fine with 2 or 3, but 8 seems to be too much. I've already raised the Requests per 100 seconds per user. too 1000.

Is there any way I could reduce the number of API calls by rewrite some of my code? Or is there another way to achieve this dashboard-kind-of-thing?

`

   <div class="container-fluid">
    <div class="row">
        <div id="auth-button"></div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="-container"></figure>
            <ol class="Chartjs-legend" id="legend-1-container"></ol>
            <div id="active-users-container1"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-2-container"></ol>
            <div id="active-users-container2"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-3-container"></ol>
            <div id="active-users-container3"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-4-container"></ol>
            <div id="active-users-container4"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-5-container"></ol>
            <div id="active-users-container5"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-6-container"></ol>
            <div id="active-users-container6"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-7-container"></ol>
            <div id="active-users-container7"></div>
        </div>
        <div class="col-lg-4 dashitem">
            <h1>name</h1>
            <figure class="Chartjs-figure" id="[name]-container"></figure>
            <ol class="Chartjs-legend" id="legend-8-container"></ol>
            <div id="active-users-container8"></div>
        </div>
    </div>

</div>


{literal}

<script>
gapi.analytics.ready(function() {

  var CLIENT_ID = '[client-id]';

  gapi.analytics.auth.authorize({
    container: 'auth-button',
    clientid: CLIENT_ID,
    userInfoLabel:""
  });


  gapi.analytics.auth.on('success', function(response) {
    //hide the auth-button
    document.querySelector("#auth-button").style.display='none';
  });

  function renderIndividualChart(id, chartId, legendId, activeUsersId) {

    // Adjust `now` to experiment with different days, for testing only...
    var now = moment(); // .subtract(3, 'day');

    var thisWeek = query({
      'ids': "ga:" + id,
      'dimensions': 'ga:date,ga:nthDay',
      'metrics': 'ga:users',
      'start-date': moment(now).subtract(1, 'day').day(0).format('YYYY-MM-DD'),
      'end-date': moment(now).format('YYYY-MM-DD')
    });

    var lastWeek = query({
      'ids': "ga:" + id,
      'dimensions': 'ga:date,ga:nthDay',
      'metrics': 'ga:users',
      'start-date': moment(now).subtract(1, 'day').day(0).subtract(1, 'week')
          .format('YYYY-MM-DD'),
      'end-date': moment(now).subtract(1, 'day').day(6).subtract(1, 'week')
          .format('YYYY-MM-DD')
    });

    Promise.all([thisWeek, lastWeek]).then(function(results) {

      var data1 = results[0].rows.map(function(row) { return +row[2]; });
      var data2 = results[1].rows.map(function(row) { return +row[2]; });
      var labels = results[1].rows.map(function(row) { return +row[0]; });

      labels = labels.map(function(label) {
        return moment(label, 'YYYYMMDD').format('ddd');
      });

      var data = {
        labels : labels,
        datasets : [
          {
            label: 'Vorige Week',
            fillColor : 'rgba(220,220,220,0.5)',
            strokeColor : 'rgba(220,220,220,1)',
            pointColor : 'rgba(220,220,220,1)',
            pointStrokeColor : '#fff',
            data : data2
          },
          {
            label: 'Deze Week',
            fillColor : 'rgba(151,187,205,0.5)',
            strokeColor : 'rgba(151,187,205,1)',
            pointColor : 'rgba(151,187,205,1)',
            pointStrokeColor : '#fff',
            data : data1
          }
        ]
      };

      new Chart(makeCanvas(chartId)).Line(data);
      generateLegend(legendId, data.datasets);
    });

    var activeUsers = new gapi.analytics.ext.ActiveUsers({
      ids: "ga:" + id,
      container: activeUsersId,
      pollingInterval: 5
    });

    //activeUsers.execute();
  }

  function query(params) {
    return new Promise(function(resolve, reject) {
      var data = new gapi.analytics.report.Data({query: params});
      data.once('success', function(response) { resolve(response); })
          .once('error', function(response) { reject(response); })
          .execute();
    });
  }

  function makeCanvas(id) {
    var container = document.getElementById(id);
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    container.innerHTML = '';
    canvas.width = container.offsetWidth;
    canvas.height = container.offsetHeight;
    container.appendChild(canvas);

    return ctx;
  }

  function generateLegend(id, items) {
    var legend = document.getElementById(id);
    legend.innerHTML = items.map(function(item) {
      var color = item.color || item.fillColor;
      var label = item.label;
      return '<li><i style="background:' + color + '"></i>' +
          escapeHtml(label) + '</li>';
    }).join('');
  }

  // Set some global Chart.js defaults.
  Chart.defaults.global.animationSteps = 60;
  Chart.defaults.global.animationEasing = 'easeInOutQuart';
  Chart.defaults.global.responsive = true;
  Chart.defaults.global.maintainAspectRatio = false;

  function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
  }


renderIndividualChart(xxx9412, '[name]-container', 'legend-1-container', 'active-users-container1');
renderIndividualChart(xxx11510, '[name]-container', 'legend-2-container', 'active-users-container2');
renderIndividualChart(xxx8011, '[name]-container', 'legend-3-container', 'active-users-container3');
renderIndividualChart(xxx4711, '[name]-container', 'legend-4-container', 'active-users-container4');
renderIndividualChart(xxx4009, '[name]-container', 'legend-5-container', 'active-users-container5');
renderIndividualChart(xxx61037, '[name]-container', 'legend-6-container', 'active-users-container6');
renderIndividualChart(xxx0969, '[name]-container', 'legend-7-container', 'active-users-container7');
renderIndividualChart(xxx5362, '[name]-container', 'legend-8-container', 'active-users-container8');

});
</script>

`

1
Have you considered just creating a Data Studio Report?Matt
Uh yes, but that's just static right? I also wanted to display the live 'active users'-count from each site.Brent NL

1 Answers

0
votes

I'm guessing you are exceeding the 10QPS per IP address limit. Since you are sending about 16 (8 * 2) calls all at once, see General Quota limits.

Be aware that there are also limits on the number of calls per day (50,000) and per Analytics View (10,000) and limits on the number of concurrent requests per view (10).

I think the easiest thing to do is to chain the calls one after the other instead of firing all the calls all at once. Depending on how fast the RPC is you might still run into the qps limits. The better thing to do is to add rate limiting on the GA data calls and also add some retry logic with exponential backoff on quota error.