Ready to Learn?Ex Libris products all provide open APIs

Tech Blog

 

Library Analytics via Google Charts and Alma Analytics API

Laura Guy on July 23rd, 2018

Introduction

Having quick access to real-time library analytics is important for all of us.

Using the Alma Analytics API Application we have set up a Springshare LibGuide using Google Charts (the URL to the LibGuide is provided below):

 

This blog post assumes you know how to set up an API Key for the Alma Analytics Application.

This blog post assumes you know how to create Alma Analytics Reports via OBIEE

This blog post also assumes you have access to a platform such as LibGuides to display your Google Charts.

This blog post often refers to api-eu.hosted.exlibrisgroup.com but if you are in North America you should use api-na.hosted.exlibrisgroup.com

Finally, this blog post is dedicated to Jim and Yoel, without whose generous help this work would have never been completed.  Thank you my friends.

 

Useful URLs:

Example of Mines Google Charts LibGuide: http://libguides.mines.edu/analytics

Google Charts: https://developers.google.com/chart/

Earlier blog post by Ido: https://developers.exlibrisgroup.com/blog/Embedding-Alma-Analytics-Reports-in-Web-Pages

Yoel's "How To" document: https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=2ahUKEwjv_MyE5LfcAhXM6oMKHcf1AXEQFjABegQIARAC&url=https%3A%2F%2Fknowledge.exlibrisgroup.com%2F%40api%2Fdeki%2Ffiles%2F63639%2FAnalytics_-_Google_charts_-_How_to_use_Alma_Analytics_with_Google_Charts_and_APIs_to_display_Analytics_reports_in_web_pages.docx%3Frevision%3D3&usg=AOvVaw09s0s5Rp-N6vuIapKP2fr3

Yoel's "Using Google Charts with Alma Analytics" section here: https://knowledge.exlibrisgroup.com/Alma/Training/Extended_Training/Presentations_and_Documents_-_Analytics

 

How It Is Done

 

In this blog post I will provide a few examples of Google Charts code that I have embedded into our LibGuides pages, in hope this is sufficient to get you started. You can right-click on the screenshots to see them full-size. I acknowledge this is not a thorough tutorial.  The intention is to provide what are hopefully useful examples.

This is complicated and challenging work.  One major complication is that the order of the data in OBIEE is not necessarily the order that is received by and displayed by Google Charts.  Tweaking is usually required.  It can be frustrating. A lot of trial and error is required.  You will see clear examples of this in the following code.

Another issue is that while there is documentation for Google Charts, it is very challenging for a non-expert-coder who is not familiar with the ways of programming.  In other words, it leaves a lot unsaid.

 

Once you have a simple Alma OBIEE Analytics Report that you would like to make available via Google Charts, you can combine the OBIEE location and title of the report in Alma Analytics with your API key to create a URL that looks like the following example (note I have changed my API Key slightly so this link will not work):

https://api-eu.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FLoans%20by%20user%20group&limit=1000&col_names=true&apikey=l7xxxx33d0d325c541d6a214d912f161c609

 

You need to be able to create a working URL like the above example that you can drop into your browser so that you can see the raw xml. 

See the "Troubleshooting" section at the bottom of this document for an example of the raw XML supplied by Analytics. That raw XML is obtained using the link above.

That is the first step to make on your way to a lovely little Google Chart.

 

The above example link points to this fairly simple Analytics Report that shows loans by patron group YTD in OBIEE. Below I show how I created this report in Analytics:

 


 

 

You'll note I combined our Internal Patron Groups:

 

This is what the LibGuide page looks like (note there are two pie charts here, the second one is the YTD loans report):

 

Once you have your report done, you can use the following code to embed the report into a Google Chart (be aware this code accomodates the two separate pie charts):

 

 

<!DOCTYPE html>

<html lang="en">

    <meta charset="utf-8" />

    

    <!--// Load Google Scripts //-->

    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

    <script type="text/javascript" src="https://www.google.com/jsapi"></script>

    

    <script type="text/javascript">




    

/* Get the User Groups Pie Chart */

        // Load the Visualization API and the piechart package.

        google.charts.load('visualization', '1.0', {'packages':['corechart']});

        

        // Set a callback to run when the Google Visualization API is loaded.

        google.charts.setOnLoadCallback(drawUserGroupsChart);

        

        // Callback that creates and populates a data table, instantiates the pie chart, passes in the data and draws it.

        function drawUserGroupsChart() {

            var xmlhttp = new XMLHttpRequest();

            xmlhttp.open("GET","https://api-eu.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FCount%20of%20Unexpired%20Users%20by%20group&limit=1000&col_names=true&apikey=l7xxxxx3d0d325c541d6a214d912f161c609",false);

            xmlhttp.send();

            

            var xmlDoc = xmlhttp.responseXML;

            var rows = xmlDoc.getElementsByTagName("Row");

            var data = new google.visualization.DataTable();

            data.addColumn('string', 'Method');

            data.addColumn('number', 'Num. of attempts');

            

            for (var i = 0; i < rows.length; i++) {

                data.addRow([

                    rows[i].getElementsByTagName("Column1")[0].childNodes[0].nodeValue,

                    parseInt(rows[i].getElementsByTagName("Column2")[0].childNodes[0].nodeValue)

                ]);

            }

            

            // Set chart options

            var options = {

                'title':'Number of Active Patrons by User Group',

                'width':800,

                'height':450

            };

            

            // Instantiate and draw our chart, passing in some options.

            var chart = new google.visualization.PieChart(document.getElementById('userGroupsChart_div'));

            chart.draw(data, options);

        }

    

    


// Set a callback to run when the Google Visualization API is loaded.

        google.charts.setOnLoadCallback(drawUserGroupsChart2);

        

        // Callback that creates and populates a data table, instantiates the pie chart, passes in the data and draws it.

        function drawUserGroupsChart2() {

            var xmlhttp = new XMLHttpRequest();

            xmlhttp.open("GET","https://api-eu.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FLoans%20by%20user%20group&limit=1000&col_names=true&apikey=l7xxxxxd0d325c541d6a214d912f161c609",false);

            xmlhttp.send();

            

            var xmlDoc = xmlhttp.responseXML;

            var rows = xmlDoc.getElementsByTagName("Row");

            var data = new google.visualization.DataTable();

            data.addColumn('string', 'Group');

            data.addColumn('number', 'Loans');

            

            for (var i = 0; i < rows.length; i++) {

                data.addRow([

                    rows[i].getElementsByTagName("Column1")[0].childNodes[0].nodeValue,

                    parseInt(rows[i].getElementsByTagName("Column2")[0].childNodes[0].nodeValue)

                ]);

            }

            

            // Set chart options

            var options = {

                'title':'Loans by User Group for all Patrons in last 365 Days',

                'width':800,

                'height':450

            };

            

            // Instantiate and draw our chart, passing in some options.

            var chart = new google.visualization.PieChart(document.getElementById('userGroupsChart2_div'));

            chart.draw(data, options);

        }

    

    
    

    </script>

    

    

    

    <style>

        caption { font-weight:bold; font-size:2em; }

        td { text-align:center; }

        .pie { max-width:50%; margin:auto; }

    </style>

</head>

<body>

    <div style="margin:auto; width:98%; text-align:center;">


        <div id="userGroupsChart_div" class="pie"><img src="https://libapps.s3.amazonaws.com/accounts/1061xx/images/loading_transparent.gif"></div>

    <div id="userGroupsChart2_div" class="pie"><img src="https://libapps.s3.amazonaws.com/accounts/10611xx/images/loading_transparent.gif"></div>

        

    </div>

</body>

</html>

 

 

Note that the above code actually builds two pie charts using two different OBIEE reports.  The URLs calling those reports have different report names.  You can see the resulting LibGuide page here:

 

http://libguides.mines.edu/c.php?g=823957&p=5882158

 

You'll notice it takes a little while to load the charts, that is normal.  We included a little "loading" graphic so that people would know that something was eventually going to happen.

 

A different example is this. it shows a table instead of a pie chart.  The table reports total number of volumes for electronic, physical, and etc. holdings:

 

 

 

And this is what that report looks like in our LibGuide:

 

Here is the code we use in our LibGuide page to create the table that you see in the previous screenshot:

 

<!DOCTYPE html>

<html lang="en">

    <meta charset="utf-8" />

    

    <!--// Load Google Scripts //-->

    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

    <script type="text/javascript" src="https://www.google.com/jsapi"></script>

    

    <script type="text/javascript">
        
            <!--// Load Package //-->

      google.charts.load('current', {'packages':['table']});
      
              <!--// Set The Callback //-->    
      
      google.charts.setOnLoadCallback(drawTableT);

      function drawTableT() {
                  var xmlhttp = new XMLHttpRequest();

xmlhttp.open("GET","https://api-na.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FTotal%20Number%20of%20Volumes&limit=1000&col_names=true&apikey=l7xxxxx3d0d325c541d6a214d912f161c609",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var rows = xmlDoc.getElementsByTagName("Row");
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Print Titles');
        data.addColumn('string', 'Digital Titles');
        data.addColumn('string', 'Electronic Collections');
        data.addColumn('string', 'Electronic Titles');
        data.addColumn('string', 'Combined Total');
for (var i = 0; i < rows.length; i++) {
    data.addRow([
    rows[i].getElementsByTagName("Column3")[0].childNodes[0].nodeValue,
    rows[i].getElementsByTagName("Column1")[0].childNodes[0].nodeValue,
    rows[i].getElementsByTagName("Column2")[0].childNodes[0].nodeValue,
    rows[i].getElementsByTagName("Column4")[0].childNodes[0].nodeValue,
    rows[i].getElementsByTagName("Column5")[0].childNodes[0].nodeValue
    
    ]);
}

        var table = new google.visualization.Table(document.getElementById('tableT_div1'));

        table.draw(data, {showRowNumber: false, width: '70%', height: '10%'});
      }
      
      
      
      
          
    </script>
  </head>
 
  <h3><left>Total Number of Titles</left></h3>
  <body>
    <div id="tableT_div1"><img src="https://libapps.s3.amazonaws.com/accounts/1061xx/images/loading_transparent.gif"></div>
  </body>
</html>

 

You can see the actual LibGuide page here:

http://libguides.mines.edu/c.php?g=823957&p=5882162

 

A third and final example is for a Google Charts bar chart.

 

Here is the Analytics OBIEE report:

 

And here is what it looks like in Analytics.  Note that while this is a table, the Google Chart will be a bar chart:

 

 

And here is the LibGuide page:

 

The following is the code embedded in LibGuides to create that bar chart. Note like in a previous example this code is actually for two Google Charts, a table and the bar chart.  If you have more than one chart on a LibGuide you need to be very careful to use unique naming everywhere (that is a normal coding caveat but something a novice may not be aware of):

 

<!DOCTYPE html>

<html lang="en">

    <meta charset="utf-8" />

    

    <!--// Load Google Scripts //-->

    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>

    <script type="text/javascript" src="https://www.google.com/jsapi"></script>

    

    <script type="text/javascript">



/* Get the Number of Loans Table */    

        google.charts.load('current', {'packages':['table']});

        google.charts.setOnLoadCallback(drawNumberOfLoansTable0);        

        

        function drawNumberOfLoansTable0() {

            var xmlhttp = new XMLHttpRequest();

            xmlhttp.open("GET","https://api-na.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FCount%20of%20Loans%20and%20Renewals%20YTD&limit=1000&col_names=true&apikey=l7xxxxx3d0d325c541d6a214d912f161c609",false);
            xmlhttp.send();

            

            var xmlDoc = xmlhttp.responseXML;

            var rows = xmlDoc.getElementsByTagName("Row");

            var data = new google.visualization.DataTable();

            data.addColumn('string', 'Group');

            data.addColumn('number', 'Loans');

            data.addColumn('number', 'Renewals');

                        data.addColumn('number', 'Auto Renewals');


            

            for (var i = 0; i < rows.length; i++) {

                data.addRow([

                    rows[i].getElementsByTagName("Column1")[0].childNodes[0].nodeValue,

                    Number(rows[i].getElementsByTagName("Column3")[0].childNodes[0].nodeValue),

                                        Number(rows[i].getElementsByTagName("Column4")[0].childNodes[0].nodeValue),

                    Number(rows[i].getElementsByTagName("Column2")[0].childNodes[0].nodeValue)
                ]);

            }

            

            var table = new google.visualization.Table(document.getElementById('numberOfLoans0_div'));

            table.draw(data, {showRowNumber: false, sortColumn: 0, sortAscending: true, width: '50%', height: '10%'});

            

            var tables = document.getElementsByClassName('google-visualization-table-table');

            var my_table = tables[0];

            var caption = my_table.createCaption();

caption.innerHTML = "Loans and Renewals YTD";

        }
        
        

    

    /* Get the User Groups Pie Chart */
        // Load the Visualization API and the piechart package.
        google.charts.load('visualization', '1.0', {'packages':['corechart', 'line']});
        
        // Set a callback to run when the Google Visualization API is loaded.
        google.charts.setOnLoadCallback(drawChart_linkResolvers000);
        
        // Callback that creates and populates a data table, instantiates the pie chart, passes in the data and draws it.
        function drawChart_linkResolvers000() {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open("GET","https://api-na.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FMonthly%20Loans&limit=1000&col_names=true&apikey=l7xxxxx3d0d325c541d6a214d912f161c609",false);
            xmlhttp.send();
            
            var xmlDoc = xmlhttp.responseXML;
            var rows = xmlDoc.getElementsByTagName("Row");
            var data = new google.visualization.DataTable();
            
            /* I only need 2 of these, one for each axis */
            data.addColumn('date', 'Usage Date'); /* horizontal axis */
            data.addColumn('number', 'Total Loans'); /* vertical axis */
            
            for (var i = 0; i < rows.length; i++) {
                var jims_year = parseInt(rows[i].getElementsByTagName("Column3")[0].childNodes[0].nodeValue);
                var jims_month = parseInt(rows[i].getElementsByTagName("Column1")[0].childNodes[0].nodeValue);
                jims_month--; // month minus one
                
                /* The following is just in case we subtract below 0 to -1, which javascript would not recognize as a month.
                of course, if I drop January to December of the previous year, I have to decrement the year, too, right... */
                if(jims_month<0) {
                    jims_month=11;    // december
                    jims_year--;    // previous year
                }
                /* Finished adjusting for months below zero */
                
                /* This creates a javascript date object */
                var jims_date = new Date(jims_year, jims_month, 1);
                
                data.addRow([
                    jims_date,
                    parseInt(rows[i].getElementsByTagName("Column4")[0].childNodes[0].nodeValue)
                ]);
            }
            
            //alert (JSON.stringify(data));
            
            // Set chart options
            var options = {
                'title':'Loans for Previous Twelve Months',
                'width':1200,
                'height':450,
                'format':'none'
            };
            
            // Instantiate and draw our chart, passing in some options.
            var chart = new google.visualization.ColumnChart(document.getElementById('linkResolversChart000_div'));
            chart.draw(data, options);
        }

    </script>

    

    

    

    <style>

        caption { font-weight:bold; font-size:2em; }


td.google-visualization-table-td { text-align:left; }

        .pie { max-width:50%; margin:auto; }

    </style>

</head>

<body>

    <div style="margin:auto; width:98%; text-align:center;">

        
    <div id="linkResolversChart000_div"><img src="https://libapps.s3.amazonaws.com/accounts/1061xx/images/loading_transparent.gif"></div>
        <div id="numberOfLoans0_div"><img src="https://libapps.s3.amazonaws.com/accounts/1061xx/images/loading_transparent.gif"></div>
        <p></p>

        


        

    </div>

</body>

</html>

 

Here is the link to the LibGuide page:

http://libguides.mines.edu/c.php?g=823957&p=5887634

 

 

Troubleshooting

As mentioned, this is a complicated business filled with potential challenges.

Here's an example of the raw XML that comes out of Analyics.  My Google Charts code relates directly to the XML. 

In this part of your Google Charts javascript code, the "Column1" and etc. come from the Analytics XML, you don't just make it up.

 

The Developers Tools associated with browsers such as Chrome and Firefox, and the Chrome Advanced REST Client may be helpful. 

 

Again, it may be helpful to come up with a URL such as what was mentioned earlier:

https://api-eu.hosted.exlibrisgroup.com/almaws/v1/analytics/reports?path=%2Fshared%2FColorado%20School%20of%20Mines%2FReports%2FAPIs%2FCount%20of%20Unexpired%20Users%20by%20group&limit=1000&col_names=true&apikey=l7xxxxx3d0d325c541d6a214d912f161c609

 

And then use the browser developer tools and Chrome Advanced REST Client to troubleshoot issues.