Commit dae87a07 authored by khawkins98's avatar khawkins98
Browse files

Initial EMBL news proof of concept

parent fcf1f853
/**
* Create a new canvas inside the specified element. Set it to be the width
* and height of its container.
* @param {string} id The id attribute of the element to host the canvas.
* @return {RenderingContext} The 2D canvas context.
*/
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;
}
/**
* Create a visual legend inside the specified element based off of a
* Chart.js dataset.
* @param {string} id The id attribute of the element to host the legend.
* @param {Array.<Object>} items A list of labels and colors for the legend.
*/
function generateLegend(id, items) {
var legend = document.getElementById(id);
legend.innerHTML = items.map(function(item) {
var color = item.color || item.strokeColor;
var label = item.label;
return '<li><i style="background:' + color + '"></i>' + 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;
\ No newline at end of file
// In short: fetches data from google API, show on dashboard
// you need to authenticate in your local JS
// extract the pub date from the url
function parsePublicationDate(url) {
var yearMonth = url.split('/')[2].substring(0,4);
yearMonth = '20' + yearMonth;
var year = yearMonth.slice(0,-2);
var month = yearMonth.slice(-2);
return month + '/' + year;
}
// == NOTE ==
// This code uses ES6 promises. If you want to use this code in a browser
// that doesn't supporting promises natively, you'll have to include a polyfill.
function analyticsAuthorize(clientid) {
// Authorize the user immediately if the user has already granted access.
// If no access has been created, render an authorize button inside the
// element with the ID "embed-api-auth-container".
gapi.analytics.auth.authorize({
container: 'embed-api-auth-container',
clientid: clientid
});
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// check if the request has expired (that is: the user changed the params)
function requestIsExpired(requestDate) {
console.log(moment(requestDate).format('YYYY-MM-DD'),document.getElementById("originDate").value);
if (moment(requestDate).format('YYYY-MM-DD') != document.getElementById("originDate").value) {
console.log('expired');
return true; // if the dates are out of sync, request has expired
}
return false; // request is still valid
}
var render_queue_time = 1;
// Queue up the requests to not exceed GA's requests per second (10 per second, per IP), 50,000 a day
// https://developers.google.com/analytics/devguides/config/mgmt/v3/limits-quotas#general_api
function render_queue(target,processor,dimensions,metrics,filters,shared) {
render_queue_time = render_queue_time + 500; // set requests XXms second apart
if (processor == "traffic-overview") {
window.setTimeout(function(){
render_query_traffic_overview(target,dimensions,metrics,filters,shared);
},render_queue_time);
}
if (processor == "overview-list") {
window.setTimeout(function(){
render_query_overview(target,dimensions,metrics,filters,shared);
},render_queue_time);
}
if (processor == "page-detail") {
window.setTimeout(function(){
render_query_page_detail(target,dimensions,metrics,filters,shared);
},render_queue_time);
}
if (processor == "page-time") {
window.setTimeout(function(){
render_query_page_time(target,dimensions,metrics,filters,shared);
},render_queue_time);
}
if (processor == "leave-rate") {
window.setTimeout(function(){
render_query_leave_rate(target,dimensions,metrics,filters,shared);
},render_queue_time);
}
}
function render_query_traffic_overview(target,dimensions,metrics,filters,shared) {
var now = shared['originDate']; // .subtract(3, 'day');
var week1 = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 8,
// 'sort': '-'+metrics,
'start-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').format('YYYY-MM-DD')
});
var week2 = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 8,
// 'sort': '-'+metrics,
'start-date': moment(now).subtract(15, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD')
});
Promise.all([week1,week2]).then(function(results) {
// console.log(results);
var labels = new Array();
for (var i = 0; i < results[0].rows.length; i++) {
labels.push(results[0].rows[i]);
}
// var labels = [results[0].rows[0],'Feb','Mar','Apr','May','Jun'];
labels = labels.map(function(label) {
return moment(label, 'YYYYMMDD').format('MMM D (ddd)');
});
var data1 = results[0].rows.map(function(row) { return +row[2]; });
var data2 = results[1].rows.map(function(row) { return +row[2]; });
var data = {
labels : labels,
datasets : [
{
label: 'This weeks\'s page views of all articles',
fillColor : 'rgba(120,220,220,0.15)',
strokeColor : 'rgba(120,220,220,1)',
pointColor : 'rgba(120,220,220,1)',
pointStrokeColor : '#fff',
data : data1
},
{
label: 'Last week',
fillColor : 'rgba(220,220,220,0.35)',
strokeColor : 'rgba(220,220,220,1)',
pointColor : 'rgba(220,220,220,1)',
pointStrokeColor : '#fff',
data : data2
},
]
};
new Chart(makeCanvas('chart-1-container')).Line(data);
generateLegend('legend-1-container', data.datasets);
// $('#chart-1-container').prepend('<div class="label">text</div>');
});
}
function render_query_overview(target,dimensions,metrics,filters,shared) {
if (requestIsExpired(shared['originDate'])) { return; }
var now = shared['originDate']; // .subtract(3, 'day');
var localQuery = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 8,
'sort': '-'+metrics,
'start-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').format('YYYY-MM-DD')
});
Promise.all([localQuery]).then(function(results) {
$('h2.local-title').html('This week vs last');
$('.local-description').html('' + results[0].query['start-date'] + ' to ' + results[0].query['end-date'] + '');
// once we know the top stories, perform queries about them
var receivedData = results[0].rows;
for (var i = 0; i < receivedData.length; i++) {
$(target).append('<tr class="result-'+ i + '">' +
'<td>' + receivedData[i][0] +
'<br/><small><a target="_blank" href="http://news.embl.de' + receivedData[i][1] + '">'+ receivedData[i][1]+'</a></small></td>' +
'<td>' + parsePublicationDate(receivedData[i][1]) + '</td>' +
'<td>' + receivedData[i][2] + '</td><td class="tr-referals small"></td><td class="tr-time-on-page"></td><td class="tr-leave-rate"></td></tr>');
render_queue('tr.result-'+i,'page-detail','ga:fullReferrer','ga:uniquePageviews',
'ga:pagePath=='+receivedData[i][1],shared);
render_queue('tr.result-'+i,'page-time','ga:pagePath','ga:avgTimeOnPage',
'ga:pagePath=='+receivedData[i][1],shared);
render_queue('tr.result-'+i,'leave-rate','ga:pagePath','ga:bounceRate',
'ga:pagePath=='+receivedData[i][1],shared);
}
});
}
// convert facebook.com/ to Facebook, etc.
function parseReferralName(siteToParse) {
var original = ['facebook.com/']
var replacements = ['Facebook']
for (var i = 0; i < original.length; i++) {
if (siteToParse.indexOf(original[i]) >= 0) {
return replacements[i];
}
}
// no match, return what it was:
return siteToParse;
}
function render_query_page_detail(target,dimensions,metrics,filters,shared) {
if (requestIsExpired(shared['originDate'])) { return; }
var now = shared['originDate']; // .subtract(3, 'day');
var localQuery = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 7,
'sort': '-'+metrics,
'start-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').format('YYYY-MM-DD')
});
Promise.all([localQuery]).then(function(results) {
var receivedData = results[0].rows;
// $(target).append('<td class=""></td>');
for (var i = 0; i < receivedData.length; i++) {
$(target+' td.tr-referals').append('<tr><td>' + parseReferralName(receivedData[i][0]) +'</td><td>' + receivedData[i][1] + '</td></tr>');
}
});
}
function render_query_page_time(target,dimensions,metrics,filters,shared) {
if (requestIsExpired(shared['originDate'])) { return; }
var now = shared['originDate']; // .subtract(3, 'day');
var localQuery = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 1,
'sort': '-'+metrics,
'start-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').format('YYYY-MM-DD')
});
Promise.all([localQuery]).then(function(results) {
var receivedData = results[0].rows;
var timeOnPage = Math.round((receivedData[0][1] / 60) * 100) / 100;
$(target + ' td.tr-time-on-page').append('' + timeOnPage );
});
}
function render_query_leave_rate(target,dimensions,metrics,filters,shared) {
if (requestIsExpired(shared['originDate'])) { return; }
var now = shared['originDate']; // .subtract(3, 'day');
var localQuery = query({
'ids': shared['viewID'],
'dimensions': dimensions,
'metrics': metrics,
'filters': filters,
'max-results': 1,
'sort': '-'+metrics,
'start-date': moment(now).subtract(8, 'day').format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').format('YYYY-MM-DD')
});
Promise.all([localQuery]).then(function(results) {
console.table(results[0].rows);
var receivedData = results[0].rows;
var leaveRate = Math.round((receivedData[0][1]) * 100) / 100;
$(target + ' td.tr-leave-rate').append('' + leaveRate +'% ');
// update table sorting
$("#table-report").trigger('update');
// add pie graph
var uniqueID = Math.floor(Math.random() * 7);
$(target + ' td.tr-leave-rate').prepend('<div id="leave-container-'+uniqueID+'"></div>');
var labels = new Array();
var data = [];
var colors = ['rgb(218,15,33)','rgb(109,171,73)','#D4CCC5','#E2EAE9','#F7464A'];
// add the negative non 100% portion
data.push({
label: results[0].rows[0][1],
value: +(1-(results[0].rows[0][1]/100)),
color: colors[1]
});
results[0].rows.forEach(function(row, i) {
data.push({
label: row[0],
value: +row[1]/100,
color: colors[i]
});
});
new Chart(makeCanvas('leave-container-'+uniqueID)).Pie(data);
});
}
/**
* Extend the Embed APIs `gapi.analytics.report.Data` component to
* return a promise the is fulfilled with the value returned by the API.
* @param {Object} params The request parameters.
* @return {Promise} A promise.
*/
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();
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> Analytics dashboards &lt; EBI Pattern library &lt; EMBL-EBI</title>
<meta name="description" content="EMBL-EBI"><!-- Describe what this page is about -->
<meta name="keywords" content="analytics bioinformatics, europe, institute, ebi"><!-- A few keywords that relate to the content of THIS PAGE (not the whol project) -->
<meta name="author" content="EMBL-EBI"><!-- Your [project-name] here -->
<meta name="HandheldFriendly" content="true" />
<meta name="MobileOptimized" content="width" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="theme-color" content="#70BDBD"> <!-- Android Chrome mobile browser tab color -->
<!-- Add information on the life cycle of this page -->
<meta name="ebi:owner" content="Web Development"> <!-- Who should be contacted about changes -->
<meta name="ebi:review-cycle" content="180"> <!-- In days, how often should the content be reviewed -->
<meta name="ebi:last-review" content="2016-11-10"> <!-- The last time the content was reviewed -->
<meta name="ebi:expiry" content="2018-11-10"> <!-- When this content is no longer relevant -->
<!-- If you link to any other sites frequently, consider optimising performance with a DNS prefetch -->
<link rel="dns-prefetch" href="//ebi.ac.uk" />
<!-- If you have custom icon, replace these as appropriate.
You can generate them at realfavicongenerator.net -->
<link rel="icon" type="image/x-icon" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/favicon.ico" />
<link rel="icon" type="image/png" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="192×192" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/android-chrome-192x192.png" /> <!-- Android (192px) -->
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/apple-icon-114x114.png"> <!-- For iPhone 4 Retina display (114px) -->
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/apple-icon-72x72.png"> <!-- For iPad (72px) -->
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/apple-icon-144x144.png"> <!-- For iPad retinat (144px) -->
<link rel="apple-touch-icon-precomposed" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/apple-icon-57x57.png"> <!-- For iPhone (57px) -->
<link rel="mask-icon" href="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/safari-pinned-tab.svg" color="#ffffff"> <!-- Safari icon for pinned tab -->
<meta name="msapplication-TileColor" content="#2b5797"> <!-- MS Icons -->
<meta name="msapplication-TileImage" content="https://www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/logos/EMBL-EBI/favicons/mstile-144x144.png">
<!-- CSS: implied media=all -->
<!-- CSS concatenated and minified via ant build script-->
<link rel="stylesheet" href="//www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/libraries/foundation-6/css/foundation.css" type="text/css" media="all">
<link rel="stylesheet" href="//www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/css/ebi-global.css" type="text/css" media="all">
<link rel="stylesheet" href="//www.ebi.ac.uk/web_guidelines/EBI-Icon-fonts/v1.1/fonts.css" type="text/css" media="all">
<!-- Use this CSS file for any custom styling -->
<!--
<link rel="stylesheet" href="css/custom.css" type="text/css" media="all">
-->
<!-- If you have a custom header image or colour -->
<!--
-->
<meta name="ebi:localmasthead-color" content="#091314">
<meta name="ebi:localmasthead-image" content="//www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/images/backgrounds/embl-ebi-background-4.jpg">
<!-- you can replace this with theme-[projectname].css. See http://www.ebi.ac.uk/web/style/colour for details of how to do this -->
<!-- also inform ES so we can host your colour palette file -->
<link rel="stylesheet" href="//www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/css/theme-embl-petrol.css" type="text/css" media="all">
<!-- for production the above can be replaced with -->
<!--
<link rel="stylesheet" href="//www.ebi.ac.uk/web_guidelines/css/compliance/mini/ebi-fluid-embl.css">
-->
<!-- end CSS-->
<!-- All JavaScript at the bottom, except for Modernizr -->
<script src="//www.ebi.ac.uk/web_guidelines/EBI-Framework/v1.1/libraries/modernizr/modernizr.custom.49274.js"></script>
</head>
<body>
<div id="skip-to">
<ul>
<li><a href="#content">Skip to main content</a></li>
<li><a href="#local-nav">Skip to local navigation</a></li>
<li><a href="#global-nav">Skip to EBI global navigation menu</a></li>
<li><a href="#global-nav-expanded">Skip to expanded EBI global navigation menu (includes all sub-sections)</a></li>
</ul>
</div>
<div data-sticky-container>
<div id="local-masthead" data-sticky data-sticky-on="large" data-top-anchor="180" data-btm-anchor="300000">
<header>
<div id="global-masthead" class="clearfix">
<!--This has to be one line and no newline characters-->
<a href="//www.ebi.ac.uk/" title="Go to the EMBL-EBI homepage"><span class="ebi-logo"></span></a>
<nav>
<div class="row">
<ul id="global-nav" class="menu">
<!-- set active class as appropriate -->
<li id="home-mobile" class=""><a href="//www.ebi.ac.uk"></a></li>
<li id="home" class="active"><a href="//www.ebi.ac.uk"><i class="icon icon-generic" data-icon="H"></i> EMBL-EBI</a></li>
<li id="services"><a href="//www.ebi.ac.uk/services"><i class="icon icon-generic" data-icon="("></i> Services</a></li>
<li id="research"><a href="//www.ebi.ac.uk/research"><i class="icon icon-generic" data-icon=")"></i> Research</a></li>
<li id="training"><a href="//www.ebi.ac.uk/training"><i class="icon icon-generic" data-icon="t"></i> Training</a></li>
<li id="about"><a href="//www.ebi.ac.uk/about"><i class="icon icon-generic" data-icon="i"></i> About us</a></li>
<li id="search">
<a href="#" data-toggle="search-global-dropdown"><i class="icon icon-functional" data-icon="1"></i> <span class="show-for-small-only">Search</span></a>
<div id="search-global-dropdown" class="dropdown-pane" data-dropdown data-options="closeOnClick:true;">
<form id="global-search" name="global-search" action="/ebisearch/search.ebi" method="GET">
<fieldset>
<div class="input-group">
<input type="text" name="query" id="global-searchbox" class="input-group-field" placeholder="Search all of EMBL-EBI">
<div class="input-group-button">
<input type="submit" name="submit" value="Search" class="button">
<input type="hidden" name="db" value="allebi" checked="checked">
<input type="hidden" name="requestFrom" value="global-masthead" checked="checked">
</div>
</div>
</fieldset>
</form>
</div>
</li>
<li class="float-right show-for-medium embl-selector">
<button class="button float-right" type="button" data-toggle="embl-dropdown">Hinxton</button>
<!-- The dropdown menu will be programatically added by script.js -->
</li>
</ul>
</div>
</nav>
</div>
<div class="masthead row">
<!-- local-title -->
<div class="columns medium-7" id="local-title">
<h1><a href="//ebiwd.github.io/EBI-Pattern-library" title="Back to EBI Pattern library homepage">EMBL Weekly news dashbaord</a></h1>
</div>
<!-- /local-title -->
<!-- local-nav -->
<nav >
<ul class="dropdown menu float-left" data-description="navigational" data-dropdown-menu>
<li class="active"><a href="//ebiwd.github.io/EBI-Pattern-library/" class="active">Dashboard</a></li>
</ul>
</nav>
<!-- /local-nav -->
</div>
</header>
</div>
</div>
<div id="content" role="main" class="row">
<section>
<!-- Your menu structure should make a breadcrumb redundant, but if a breadcrump is needed uncomment the below -->
<!--
<nav aria-label="You are here:" role="navigation">
<ul class="breadcrumbs">
<li><a href="#">Home</a></li>
<li><a href="#">Parent</a></li>
<li class="disabled">Disabled</li>
<li>
<span class="show-for-sr">Current: </span> This page
</li>
</ul>
</nav>
-->
<div class="row" id="main-content-area">
<ul class="accordion" data-accordion data-multi-expand="true">
<li class="accordion-item" data-accordion-item>
<a href="#" class="accordion-title">Conceptual change list</a>
<div class="accordion-content" data-tab-content>
<ul>
<li>JS link/event tracking</li>
<ul>
<li> Need to add list of outbound link clicks/next link clicks with link tracking</li>
<li>Additional metrics and info might come in handy, such as pages (on the EMBL websites) visited from this page, searches performed, other actions taken (e.g. share button clicked, newsletter subscription etc.).</li>
</ul>
<li> Perhaps we should add desktop vs mobile stats per article?</li>
</ul>
</div>
</li> <li class="accordion-item" data-accordion-item>
<a href="#" class="accordion-title">Polishing list</a>
<div class="accordion-content" data-tab-content>
<ul>
<li>Referers</li>
<ul>
<li>Perhaps some of these could get custom aliases? E.g. Google, Facebook, Facebook (mobile), Twitter, LinkedIn plus mail campaign tools etc. instead of providing the link. In addition, how about grouping them visually (e.g. to show all social media referrals together) and providing aggregated stats for those groups?</li>
<li>polish display of URls</li>
<li>merge mobile and desktop into one (i.e. Facebook vs m.facebook)</li>
</ul>
<li>And I’m wondering whether we should have a line in the chart pointing to an average traffic..or the green/orange/red indicator showing how the number of views relate to the average article... Like the idea of various “averages”. Instinct suggest that metric should come from a human — if you will: targets for news stories for # of visits, bounce rate, time on page, etc.</li>
<li>Lock date selector to weeks</li>
<li>Dev tasks</li>
<ul>
<li>Move to version control (github is most colab)</li>
<li>Optimise code structure</li>
<li>Expose somewhere for others to access. gh-pages? (users will still need to login to GA)</li>
</ul>
</ul>
</div>
</li>
<li class="accordion-item is-active" data-accordion-item>
<a href="#" class="accordion-title">Options</a>
<div class="accordion-content" data-tab-content>
<label for="originDate">Pick an end date for the week's report</label>
<p class="small">Note, the report period will round to weekly units. Format is DD-MM-YYYY.</p>
<input id="originDate" type="date" value=""/>
</div>
</li>
</ul>
<!-- Via: https://ga-dev-tools.appspot.com/embed-api/third-party-visualizations/ -->
<!-- Load the Embed API library. -->
<script>
(function(w,d,s,g,js,fs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(f){this.q.push(f);}};
js=d.createElement(s);fs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';