Skip to content

Commit 8fb5f96

Browse files
Add D3.js line chart for views over time on metrics page
1 parent c5c78f1 commit 8fb5f96

File tree

3 files changed

+1563
-802
lines changed

3 files changed

+1563
-802
lines changed

physionet-django/project/templates/project/published_project_metrics.html

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,54 @@
66
Metrics for {{ project }}
77
{% endblock %}
88

9+
{% block local_js_top %}
10+
<script src="{% static 'd3/d3.v3.min.js' %}"></script>
11+
<script src="{% static 'd3/d3.tip.v0.6.3.js' %}"></script>
12+
{% endblock %}
13+
14+
{% block local_css %}
15+
<style>
16+
#views-chart .axis path,
17+
#views-chart .axis line {
18+
fill: none;
19+
stroke: #333;
20+
shape-rendering: crispEdges;
21+
}
22+
#views-chart .axis text {
23+
font-size: 12px;
24+
}
25+
#views-chart .line {
26+
fill: none;
27+
stroke: steelblue;
28+
stroke-width: 2px;
29+
}
30+
#views-chart .dot {
31+
fill: steelblue;
32+
stroke: #fff;
33+
stroke-width: 1.5px;
34+
}
35+
.d3-tip {
36+
line-height: 1;
37+
padding: 8px;
38+
background: rgba(0, 0, 0, 0.8);
39+
color: #fff;
40+
border-radius: 4px;
41+
font-size: 13px;
42+
}
43+
</style>
44+
{% endblock %}
45+
946
{% block content %}
1047
<div class="container">
1148
<h1>Metrics</h1>
1249
<p class="text-muted">{{ project.title }} (v{{ project.version }})</p>
1350
<hr>
1451

15-
<div class="row">
16-
<div class="col-md-6">
17-
<div class="card text-center mb-4">
52+
<div class="row justify-content-center">
53+
<div class="col-md-8 col-lg-6">
54+
<div id="views-card" class="card text-center mb-4 d-flex justify-content-center" style="min-height: 250px;">
1855
<h5 class="card-header">Unique Registered Project Views</h5>
19-
<div class="card-body">
56+
<div class="card-body d-flex flex-column justify-content-center">
2057
<div class="d-flex justify-content-around mb-2">
2158
<div>
2259
<h3 class="mb-0">{{ project_views_count }}</h3>
@@ -31,6 +68,88 @@ <h3 class="mb-0">{{ all_versions_views_count }}</h3>
3168
</div>
3269
</div>
3370
</div>
71+
{% if views_over_time %}
72+
<div class="col-md-8 col-lg-6">
73+
<div id="views-chart" class="mb-4 d-flex justify-content-center"></div>
74+
<script>
75+
(function() {
76+
var data = {{ chart_data|safe }};
77+
var parseDate = d3.time.format('%b %Y').parse;
78+
data.forEach(function(d) {
79+
d.date = parseDate(d.month);
80+
});
81+
82+
var cardHeight = document.getElementById('views-card').offsetHeight;
83+
var margin = {top: 20, right: 20, bottom: 50, left: 50},
84+
width = document.getElementById('views-chart').offsetWidth - margin.left - margin.right,
85+
height = cardHeight - margin.top - margin.bottom;
86+
87+
var x = d3.time.scale().range([0, width]);
88+
var y = d3.scale.linear().range([height, 0]);
89+
90+
var xAxis = d3.svg.axis().scale(x).orient('bottom')
91+
.tickFormat(d3.time.format('%b %Y'));
92+
var yAxis = d3.svg.axis().scale(y).orient('left')
93+
.ticks(6).tickFormat(d3.format('d'));
94+
95+
var line = d3.svg.line()
96+
.x(function(d) { return x(d.date); })
97+
.y(function(d) { return y(d.count); });
98+
99+
var tip = d3.tip()
100+
.attr('class', 'd3-tip')
101+
.offset([-10, 0])
102+
.html(function(d) {
103+
return '<strong>' + d.month + '</strong>: ' + d.count + ' viewers';
104+
});
105+
106+
var svg = d3.select('#views-chart').append('svg')
107+
.attr('width', width + margin.left + margin.right)
108+
.attr('height', height + margin.top + margin.bottom)
109+
.append('g')
110+
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
111+
112+
svg.call(tip);
113+
114+
x.domain(d3.extent(data, function(d) { return d.date; }));
115+
y.domain([0, d3.max(data, function(d) { return d.count; })]);
116+
117+
svg.append('g')
118+
.attr('class', 'x axis')
119+
.attr('transform', 'translate(0,' + height + ')')
120+
.call(xAxis)
121+
.selectAll('text')
122+
.attr('transform', 'rotate(-45)')
123+
.style('text-anchor', 'end');
124+
125+
svg.append('g')
126+
.attr('class', 'y axis')
127+
.call(yAxis)
128+
.append('text')
129+
.attr('transform', 'rotate(-90)')
130+
.attr('x', -height / 2)
131+
.attr('y', -margin.left + 14)
132+
.style('text-anchor', 'middle')
133+
.text('Unique Viewers');
134+
135+
svg.append('path')
136+
.datum(data)
137+
.attr('class', 'line')
138+
.attr('d', line);
139+
140+
svg.selectAll('.dot')
141+
.data(data)
142+
.enter().append('circle')
143+
.attr('class', 'dot')
144+
.attr('cx', function(d) { return x(d.date); })
145+
.attr('cy', function(d) { return y(d.count); })
146+
.attr('r', 4)
147+
.on('mouseover', tip.show)
148+
.on('mouseout', tip.hide);
149+
})();
150+
</script>
151+
</div>
152+
{% endif %}
34153
</div>
35154

36155
{% if views_by_version|length > 1 %}

0 commit comments

Comments
 (0)