mirror of https://github.com/status-im/fathom.git
wip on chart implementation using d3
This commit is contained in:
parent
da19c116b7
commit
7adc5d3bec
|
@ -0,0 +1,134 @@
|
|||
'use strict';
|
||||
|
||||
import { h, Component } from 'preact';
|
||||
import Client from '../lib/client.js';
|
||||
import { bind } from 'decko';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
function padData(data) {
|
||||
for(let i=0; i<data.length; i++) {
|
||||
data[i].Date = data[i].Date.substring(0, 10);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Chart extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
data: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, prevState) {
|
||||
if(newProps.before == prevState.before && newProps.after == prevState.after) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchData(newProps.before, newProps.after);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
var padding = 20,
|
||||
h = 300,
|
||||
w = this.base.clientWidth - ( padding * 3);
|
||||
|
||||
this.vis = d3.select(this.base)
|
||||
.append('svg')
|
||||
.attr('width', w + (padding * 2))
|
||||
.attr('height', h + (padding * 2))
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + (2*padding) + ',' + padding + ')');
|
||||
}
|
||||
|
||||
@bind
|
||||
redrawChart() {
|
||||
var data = this.state.data;
|
||||
var max = d3.max(data, (d) => d.Pageviews);
|
||||
|
||||
var h = 300,
|
||||
padding = 20,
|
||||
w = this.base.clientWidth - ( padding * 3),
|
||||
x = d3.scaleBand().range([0, w]).padding(0.2).round(true),
|
||||
y = d3.scaleLinear().range([h, 0]),
|
||||
|
||||
yAxis = d3.axisLeft().scale(y),
|
||||
xAxis = d3.axisBottom().scale(x);
|
||||
|
||||
x.domain(data.map((d) => d.Date))
|
||||
y.domain([0, (max * 1.1)])
|
||||
|
||||
// clear all previous data
|
||||
this.vis.selectAll('*').remove();
|
||||
|
||||
// axes
|
||||
this.vis.append("g")
|
||||
.attr("class", "y axis")
|
||||
.call(yAxis);
|
||||
|
||||
this.vis.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr('transform', 'translate(0,' + h + ')')
|
||||
.call(xAxis)
|
||||
.selectAll('g')
|
||||
|
||||
// bars
|
||||
var bars = this.vis.selectAll('g.pageviews')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'pageviews')
|
||||
.attr('transform', function (d, i) { return "translate(" + x(d.Date) + ", 0)" });
|
||||
|
||||
bars.append('rect')
|
||||
.attr('width', x.bandwidth())
|
||||
.attr('height', (d) => (h - y(d.Pageviews)) )
|
||||
.attr('y', (d) => y(d.Pageviews))
|
||||
|
||||
// visitors
|
||||
var visitorBars = this.vis.selectAll('g.visitors')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'visitors')
|
||||
.attr('transform', function (d, i) { return "translate(" + ( x(d.Date) + 0.25 * x.bandwidth() ) + ", 0)" });
|
||||
|
||||
visitorBars.append('rect')
|
||||
.attr('width', x.bandwidth() * 0.5)
|
||||
.attr('height', (d) => (h - y(d.Visitors)) )
|
||||
.attr('y', (d) => y(d.Visitors))
|
||||
|
||||
}
|
||||
|
||||
@bind
|
||||
fetchData(before, after) {
|
||||
this.setState({ loading: true })
|
||||
|
||||
Client.request(`/stats/site/groupby/day?before=${before}&after=${after}`)
|
||||
.then((d) => {
|
||||
// request finished; check if timestamp range is still the one user wants to see
|
||||
if( this.props.before != before || this.props.after != after ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: padData(d),
|
||||
});
|
||||
|
||||
this.redrawChart();
|
||||
})
|
||||
}
|
||||
|
||||
render(props, state) {
|
||||
return (
|
||||
<div></div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Chart
|
|
@ -6,7 +6,7 @@ import Realtime from '../components/Realtime.js';
|
|||
import DatePicker from '../components/DatePicker.js';
|
||||
import CountWidget from '../components/CountWidget.js';
|
||||
import Table from '../components/Table.js';
|
||||
|
||||
import Chart from '../components/Chart.js';
|
||||
import { bind } from 'decko';
|
||||
|
||||
class Dashboard extends Component {
|
||||
|
@ -46,6 +46,12 @@ class Dashboard extends Component {
|
|||
<DatePicker onChange={this.changePeriod} value={state.period} />
|
||||
</nav>
|
||||
|
||||
<div class="boxes">
|
||||
<div class="box box-graph animated fadeInUp delayed_03s" style="padding: 0;">
|
||||
<Chart before={state.before} after={state.after} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxes">
|
||||
<div class="box box-totals animated fadeInUp delayed_03s">
|
||||
<CountWidget title="Unique visitors" endpoint="stats/site/visitors" before={state.before} after={state.after} />
|
||||
|
@ -56,8 +62,8 @@ class Dashboard extends Component {
|
|||
|
||||
<Table endpoint="stats/pages" headers={["Top pages", "Views", "Uniques"]} before={state.before} after={state.after} />
|
||||
<Table endpoint="stats/referrers" headers={["Top referrers", "Views", "Uniques"]} before={state.before} after={state.after} showHostname="true" />
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<footer class="section"></footer>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
g.pageviews {
|
||||
fill: #88ffc6;
|
||||
}
|
||||
|
||||
g.visitors {
|
||||
fill: #533feb;
|
||||
}
|
||||
|
||||
.axis {
|
||||
font-size: 12px;
|
||||
fill: #6c7680;
|
||||
}
|
|
@ -103,7 +103,6 @@ body {
|
|||
.main-nav ul { display: inline-block; padding: 0; }
|
||||
.spacer { color: #98a0a6; padding: 0 8px; }
|
||||
|
||||
svg { width: 24px; height: 24px; display: inline-block; vertical-align: top; }
|
||||
|
||||
.header div, .date-nav a, .total-heading { font-size: 12px; text-transform: uppercase; color: #98a0a6; }
|
||||
|
||||
|
@ -178,3 +177,4 @@ body {
|
|||
|
||||
@import "util";
|
||||
@import "pikaday";
|
||||
@import "chart";
|
||||
|
|
|
@ -1576,6 +1576,270 @@
|
|||
"array-find-index": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"d3": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-5.4.0.tgz",
|
||||
"integrity": "sha1-CQGZqFadHeI9BKP/B/4TYJX+p04=",
|
||||
"requires": {
|
||||
"d3-array": "1",
|
||||
"d3-axis": "1",
|
||||
"d3-brush": "1",
|
||||
"d3-chord": "1",
|
||||
"d3-collection": "1",
|
||||
"d3-color": "1",
|
||||
"d3-contour": "1",
|
||||
"d3-dispatch": "1",
|
||||
"d3-drag": "1",
|
||||
"d3-dsv": "1",
|
||||
"d3-ease": "1",
|
||||
"d3-fetch": "1",
|
||||
"d3-force": "1",
|
||||
"d3-format": "1",
|
||||
"d3-geo": "1",
|
||||
"d3-hierarchy": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-path": "1",
|
||||
"d3-polygon": "1",
|
||||
"d3-quadtree": "1",
|
||||
"d3-random": "1",
|
||||
"d3-scale": "2",
|
||||
"d3-scale-chromatic": "1",
|
||||
"d3-selection": "1",
|
||||
"d3-shape": "1",
|
||||
"d3-time": "1",
|
||||
"d3-time-format": "2",
|
||||
"d3-timer": "1",
|
||||
"d3-transition": "1",
|
||||
"d3-voronoi": "1",
|
||||
"d3-zoom": "1"
|
||||
}
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
|
||||
"integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
|
||||
},
|
||||
"d3-axis": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz",
|
||||
"integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo="
|
||||
},
|
||||
"d3-brush": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
|
||||
"integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=",
|
||||
"requires": {
|
||||
"d3-dispatch": "1",
|
||||
"d3-drag": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-selection": "1",
|
||||
"d3-transition": "1"
|
||||
}
|
||||
},
|
||||
"d3-chord": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
|
||||
"integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=",
|
||||
"requires": {
|
||||
"d3-array": "1",
|
||||
"d3-path": "1"
|
||||
}
|
||||
},
|
||||
"d3-collection": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz",
|
||||
"integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI="
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.0.tgz",
|
||||
"integrity": "sha512-dmL9Zr/v39aSSMnLOTd58in2RbregCg4UtGyUArvEKTTN6S3HKEy+ziBWVYo9PTzRyVW+pUBHUtRKz0HYX+SQg=="
|
||||
},
|
||||
"d3-contour": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.2.0.tgz",
|
||||
"integrity": "sha512-nDzZ2KDnrgTrhMjV8TH0RNrljk6uPNAGkG/v/1SKNVvJa2JU8szjh7o2ZYTX8yufA2oCI5HyeMqbzwiB+oDoIA==",
|
||||
"requires": {
|
||||
"d3-array": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"d3-dispatch": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
|
||||
"integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg="
|
||||
},
|
||||
"d3-drag": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz",
|
||||
"integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1",
|
||||
"d3-selection": "1"
|
||||
}
|
||||
},
|
||||
"d3-dsv": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz",
|
||||
"integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==",
|
||||
"requires": {
|
||||
"commander": "2",
|
||||
"iconv-lite": "0.4",
|
||||
"rw": "1"
|
||||
}
|
||||
},
|
||||
"d3-ease": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
|
||||
"integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4="
|
||||
},
|
||||
"d3-fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-j+V4vtT6dceQbcKYLtpTueB8Zvc+wb9I93WaFtEQIYNADXl0c1ZJMN3qQo0CssiTsAqK8pePwc7f4qiW+b0WOg==",
|
||||
"requires": {
|
||||
"d3-dsv": "1"
|
||||
}
|
||||
},
|
||||
"d3-force": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz",
|
||||
"integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==",
|
||||
"requires": {
|
||||
"d3-collection": "1",
|
||||
"d3-dispatch": "1",
|
||||
"d3-quadtree": "1",
|
||||
"d3-timer": "1"
|
||||
}
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.0.tgz",
|
||||
"integrity": "sha512-ycfLEIzHVZC3rOvuBOKVyQXSiUyCDjeAPIj9n/wugrr+s5AcTQC2Bz6aKkubG7rQaQF0SGW/OV4UEJB9nfioFg=="
|
||||
},
|
||||
"d3-geo": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.10.0.tgz",
|
||||
"integrity": "sha512-VK/buVGgexthTTqGRNXQ/LSo3EbOFu4p2Pjud5drSIaEnOaF2moc8A3P7WEljEO1JEBEwbpAJjFWMuJiUtoBcw==",
|
||||
"requires": {
|
||||
"d3-array": "1"
|
||||
}
|
||||
},
|
||||
"d3-hierarchy": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz",
|
||||
"integrity": "sha512-nn4bhBnwWnMSoZgkBXD7vRyZ0xVUsNMQRKytWYHhP1I4qHw+qzApCTgSQTZqMdf4XXZbTMqA59hFusga+THA/g=="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.2.0.tgz",
|
||||
"integrity": "sha512-zLvTk8CREPFfc/2XglPQriAsXkzoRDAyBzndtKJWrZmHw7kmOWHNS11e40kPTd/oGk8P5mFJW5uBbcFQ+ybxyA==",
|
||||
"requires": {
|
||||
"d3-color": "1"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
|
||||
"integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q="
|
||||
},
|
||||
"d3-polygon": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
|
||||
"integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI="
|
||||
},
|
||||
"d3-quadtree": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
|
||||
"integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg="
|
||||
},
|
||||
"d3-random": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz",
|
||||
"integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.0.0.tgz",
|
||||
"integrity": "sha512-Sa2Ny6CoJT7x6dozxPnvUQT61epGWsgppFvnNl8eJEzfJBG0iDBBTJAtz2JKem7Mb+NevnaZiDiIDHsuWkv6vg==",
|
||||
"requires": {
|
||||
"d3-array": "^1.2.0",
|
||||
"d3-collection": "1",
|
||||
"d3-format": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-time": "1",
|
||||
"d3-time-format": "2"
|
||||
}
|
||||
},
|
||||
"d3-scale-chromatic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.0.tgz",
|
||||
"integrity": "sha512-YwMbiaW2bStWvQFByK8hA6hk7ToWflspIo2TRukCqERd8isiafEMBXmwfh8c7/0Z94mVvIzIveRLVC6RAjhgeA==",
|
||||
"requires": {
|
||||
"d3-color": "1",
|
||||
"d3-interpolate": "1"
|
||||
}
|
||||
},
|
||||
"d3-selection": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz",
|
||||
"integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA=="
|
||||
},
|
||||
"d3-shape": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz",
|
||||
"integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=",
|
||||
"requires": {
|
||||
"d3-path": "1"
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz",
|
||||
"integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz",
|
||||
"integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
},
|
||||
"d3-timer": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz",
|
||||
"integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA=="
|
||||
},
|
||||
"d3-transition": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz",
|
||||
"integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==",
|
||||
"requires": {
|
||||
"d3-color": "1",
|
||||
"d3-dispatch": "1",
|
||||
"d3-ease": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-selection": "^1.1.0",
|
||||
"d3-timer": "1"
|
||||
}
|
||||
},
|
||||
"d3-voronoi": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
|
||||
"integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw="
|
||||
},
|
||||
"d3-zoom": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz",
|
||||
"integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1",
|
||||
"d3-drag": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-selection": "1",
|
||||
"d3-transition": "1"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
|
@ -2819,6 +3083,14 @@
|
|||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||
"dev": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.23",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz",
|
||||
|
@ -4631,6 +4903,11 @@
|
|||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
|
@ -4645,6 +4922,11 @@
|
|||
"ret": "~0.1.10"
|
||||
}
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sass-graph": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"cookies-js": "^1.2.3",
|
||||
"d3": "^5.4.0",
|
||||
"decko": "^1.2.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"pikaday": "^1.7.0",
|
||||
|
|
|
@ -13,6 +13,7 @@ func (api *API) Routes() *mux.Router {
|
|||
r.Handle("/api/session", HandlerFunc(api.LoginHandler)).Methods(http.MethodPost)
|
||||
r.Handle("/api/session", HandlerFunc(api.LogoutHandler)).Methods(http.MethodDelete)
|
||||
|
||||
r.Handle("/api/stats/site/groupby/day", api.Authorize(HandlerFunc(api.GetSiteStatsPerDayHandler))).Methods(http.MethodGet)
|
||||
r.Handle("/api/stats/site/pageviews", api.Authorize(HandlerFunc(api.GetSiteStatsPageviewsHandler))).Methods(http.MethodGet)
|
||||
r.Handle("/api/stats/site/visitors", api.Authorize(HandlerFunc(api.GetSiteStatsVisitorsHandler))).Methods(http.MethodGet)
|
||||
r.Handle("/api/stats/site/duration", api.Authorize(HandlerFunc(api.GetSiteStatsDurationHandler))).Methods(http.MethodGet)
|
||||
|
|
|
@ -52,3 +52,13 @@ func (api *API) GetSiteStatsRealtimeHandler(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
return respond(w, envelope{Data: result})
|
||||
}
|
||||
|
||||
// URL: /api/stats/site/groupby/day
|
||||
func (api *API) GetSiteStatsPerDayHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
params := GetRequestParams(r)
|
||||
result, err := api.database.GetSiteStatsPerDay(params.StartDate, params.EndDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return respond(w, envelope{Data: result})
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ type Datastore interface {
|
|||
|
||||
// site stats
|
||||
GetSiteStats(time.Time) (*models.SiteStats, error)
|
||||
GetSiteStatsPerDay(time.Time, time.Time) ([]*models.SiteStats, error)
|
||||
InsertSiteStats(*models.SiteStats) error
|
||||
UpdateSiteStats(*models.SiteStats) error
|
||||
GetTotalSiteViews(time.Time, time.Time) (int, error)
|
||||
|
|
|
@ -28,6 +28,14 @@ func (db *sqlstore) UpdateSiteStats(s *models.SiteStats) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (db *sqlstore) GetSiteStatsPerDay(startDate time.Time, endDate time.Time) ([]*models.SiteStats, error) {
|
||||
results := []*models.SiteStats{}
|
||||
sql := `SELECT * FROM daily_site_stats WHERE date >= ? AND date <= ?`
|
||||
query := db.Rebind(sql)
|
||||
err := db.Select(&results, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (db *sqlstore) GetTotalSiteViews(startDate time.Time, endDate time.Time) (int, error) {
|
||||
sql := `SELECT COALESCE(SUM(pageviews), 0) FROM daily_site_stats WHERE date >= ? AND date <= ?`
|
||||
query := db.Rebind(sql)
|
||||
|
|
Loading…
Reference in New Issue