ackee-report
Advanced tools
Comparing version 0.2.2 to 0.3.0
@@ -7,7 +7,14 @@ # Changelog | ||
## [v.2.2] - 2020-10-26 | ||
## [v0.3.0] - 2020-10-26 | ||
### Added | ||
- include all data available through API in report | ||
### Changed | ||
- use ejs to render email html | ||
- structure of data returned in report | ||
## [v0.2.2] - 2020-10-26 | ||
### Fixed | ||
- correctly calculate average duration of all domains | ||
## [v.2.1] - 2020-10-26 | ||
## [v0.2.1] - 2020-10-26 | ||
### Added | ||
@@ -14,0 +21,0 @@ - use `-d all` to generate a report for all domains |
{ | ||
"name": "ackee-report", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"description": "CLI tool to generate performance reports of websites using the self-hosted analytics tool Ackee.", | ||
@@ -26,2 +26,3 @@ "bin": "./src/index.js", | ||
"configstore": "^5.0.1", | ||
"ejs": "^3.1.5", | ||
"nodemailer": "^6.4.14", | ||
@@ -28,0 +29,0 @@ "ora": "^5.1.0", |
@@ -98,2 +98,3 @@ <div align="center"> | ||
<details><summary>Gmail</summary> | ||
If you use gmail to send emails, use these values: | ||
@@ -109,2 +110,3 @@ | ||
<details><summary>SendGrid</summary> | ||
If you use SendGrid to send emails, use these values: | ||
@@ -119,2 +121,3 @@ | ||
<details><summary>MailGun</summary> | ||
If you use SendGrid to send emails, use these values: | ||
@@ -129,2 +132,12 @@ | ||
## 📝 To do | ||
Here is what's currently planned for [ackee-report](https://github.com/BetaHuhn/ackee-report): | ||
- include more data in report | ||
- better design/structure of email report | ||
- more customization of data included in report | ||
- change config file via cli | ||
- add more services (e.g. Telegram) | ||
## 💻 Development | ||
@@ -131,0 +144,0 @@ |
@@ -70,3 +70,3 @@ const axios = require('axios') | ||
async domains() { | ||
async getDomains() { | ||
try { | ||
@@ -98,3 +98,3 @@ const query = ` | ||
async domain(id) { | ||
async _getDomainData(id) { | ||
try { | ||
@@ -107,18 +107,36 @@ const query = ` | ||
facts { | ||
averageViews | ||
averageDuration | ||
viewsMonth | ||
averageViews | ||
averageDuration | ||
viewsMonth | ||
} | ||
statistics { | ||
pages(sorting: TOP, limit:5) { | ||
id | ||
count | ||
created | ||
pages(sorting: TOP, limit:5) { | ||
id | ||
count | ||
} | ||
referrers(sorting: TOP, limit:5) { | ||
id | ||
count | ||
} | ||
languages(sorting: TOP, limit:3) { | ||
id | ||
count | ||
} | ||
browsers(sorting: TOP, type: WITH_VERSION, limit:3) { | ||
id | ||
count | ||
} | ||
devices(sorting: TOP, type: WITH_MODEL, limit:3) { | ||
id | ||
count | ||
} | ||
sizes(sorting: TOP, type: SCREEN_RESOLUTION, limit:3) { | ||
id | ||
count | ||
} | ||
systems(sorting: TOP, type: NO_VERSION, limit:3) { | ||
id | ||
count | ||
} | ||
} | ||
referrers(sorting: TOP, limit:5) { | ||
id | ||
count | ||
created | ||
} | ||
} | ||
} | ||
@@ -149,2 +167,45 @@ } | ||
async get(ids) { | ||
const getData = async () => Promise.all(ids.map((id) => this._getDomainData(id))) | ||
const data = await getData() | ||
const durationAvg = () => { | ||
const durations = data.filter((domain) => domain.facts.averageDuration > 0) | ||
const avg = data.reduce((n, { facts }) => n + facts.averageDuration, 0) / durations.length | ||
return Math.round(avg / 1000) | ||
} | ||
const names = data.map((domain) => domain.title).join(', ') | ||
const namesShort = (data.length > 2 ? data.slice(0, 2) : data).map((domain) => domain.title).join(', ') + ` and ${ data.length - 2 } more` | ||
const total = data.reduce((n, { facts }) => n + facts.viewsMonth, 0) | ||
const domains = data.map((domain) => { | ||
return { | ||
id: domain.id, | ||
title: domain.title, | ||
viewsAvgDay: domain.facts.averageViews, | ||
viewsMonth: domain.facts.viewsMonth, | ||
durationAvg: Math.round(domain.facts.averageDuration / 1000), | ||
pages: domain.statistics.pages, | ||
referrers: domain.statistics.referrers, | ||
languages: domain.statistics.languages, | ||
browsers: domain.statistics.browsers, | ||
devices: domain.statistics.devices, | ||
sizes: domain.statistics.sizes, | ||
systems: domain.statistics.systems | ||
} | ||
}) | ||
const result = { | ||
views: total, | ||
durationAvg: durationAvg(), | ||
names, | ||
namesShort, | ||
domains | ||
} | ||
return result | ||
} | ||
} | ||
@@ -151,0 +212,0 @@ |
@@ -31,3 +31,3 @@ const Ackee = require('./Interface') | ||
domains = await ackee.domains() | ||
domains = await ackee.getDomains() | ||
@@ -45,7 +45,5 @@ if (domain[0] !== 'all') { | ||
const getData = async () => Promise.all(domains.map((id) => ackee.domain(id))) | ||
spinner.text = 'Getting data...' | ||
const data = await ackee.get(domains) | ||
spinner.text = 'Getting domain data...' | ||
const data = await getData() | ||
if (service === 'email') { | ||
@@ -88,3 +86,3 @@ if (!to) return spinner.fail(' error: no recipient specified with --to') | ||
let domains = await ackee.domains() | ||
let domains = await ackee.getDomains() | ||
@@ -91,0 +89,0 @@ if (args.length > 0) { |
const nodemailer = require('nodemailer') | ||
const ejs = require('ejs') | ||
const path = require('path') | ||
const loadConfig = require('../Config') | ||
@@ -19,38 +21,8 @@ | ||
build(domains, endpoint, to) { | ||
const total = domains.reduce((n, { facts }) => n + facts.viewsMonth, 0) | ||
const durations = domains.filter((domain) => domain.facts.averageDuration > 0) | ||
const average = Math.round((durations.reduce((n, { facts }) => n + facts.averageDuration, 0) / durations.length) / 1000) | ||
const head = ` | ||
<h2>Ackee report</h2> | ||
<p>You've had ${ total } visitors in total 🎉! Each visitor stayed on you website for an average of ${ average } seconds. See below for a more detailed report of each domain:</p> | ||
async html(data, endpoint, to) { | ||
const generatedAt = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') | ||
` | ||
const html = await ejs.renderFile(path.join(__dirname, '../templates/ackee.ejs'), { domains: data.domains, total: data.views, durationAvg: data.durationAvg, names: data.names, namesShort: data.namesShort, endpoint, to, generatedAt }) | ||
const domainData = (domain) => ` | ||
<h2>${ domain.title }</h2> | ||
<p>Average views per day: ${ domain.facts.averageViews }</p> | ||
<p>Average duration: ${ domain.facts.averageDuration / 1000 } seconds</p> | ||
<p>Views this month: ${ domain.facts.viewsMonth }</p> | ||
<p>Top pages:</p> | ||
<ul>${ domain.statistics.pages.map((page) => `<li>${ page.id } - ${ page.count } visits</li>`) }</ul> | ||
<p>Top referrers:</p> | ||
<ul>${ domain.statistics.referrers.map((referrer) => `<li>${ referrer.id } - ${ referrer.count } visits</li>`).join('') }</ul> | ||
` | ||
const names = domains.map((domain) => domain.title).join(', ') | ||
const footer = ` | ||
<br> | ||
<p>View all current statistics on <a href="${ endpoint }">${ endpoint }</a></p> | ||
<p>This report was generated at ${ new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') } for ${ names } and sent to ${ to }</p> | ||
` | ||
const html = ` | ||
${ head } | ||
${ domains.map(domainData).join('') } | ||
${ footer } | ||
` | ||
return html | ||
} | ||
@@ -78,4 +50,5 @@ | ||
const report = async function(domains, to) { | ||
return new Promise((resolve) => { | ||
const report = async function(data, to) { | ||
// eslint-disable-next-line no-async-promise-executor | ||
return new Promise(async (resolve) => { | ||
const config = loadConfig() | ||
@@ -86,5 +59,5 @@ | ||
const email = new Email(host, port, username, password) | ||
const html = email.build(domains, config.get('ackee').server, to) | ||
const html = await email.html(data, config.get('ackee').server, to) | ||
const subject = `Ackee report for ${ domains.map((domain) => domain.title).join(', ') }` | ||
const subject = `Ackee report for ${ data.namesShort }` | ||
@@ -91,0 +64,0 @@ email.send(from, to, subject, html).then((info) => { |
const fs = require('fs') | ||
const report = async function(domains, output) { | ||
const report = async function(data, output) { | ||
return new Promise((resolve, reject) => { | ||
const data = JSON.stringify(domains, null, 2) | ||
const result = JSON.stringify(data, null, 2) | ||
fs.writeFile(output, data, (err) => { | ||
fs.writeFile(output, result, (err) => { | ||
if (err) reject(err) | ||
@@ -9,0 +9,0 @@ resolve() |
26016
19
403
160
7
+ Addedejs@^3.1.5
+ Addedasync@3.2.6(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.112.0.1(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedejs@3.1.10(transitive)
+ Addedfilelist@1.0.4(transitive)
+ Addedjake@10.9.2(transitive)
+ Addedminimatch@3.1.25.1.6(transitive)