From c2a7a9bd6a29cd139e4404b394e75797b787de3e Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 21 Apr 2021 15:00:32 -0500 Subject: [PATCH] Admin UI: Apply unique constraint to login metric --- frontend/static/js/admin/dashboard.js | 9 ++++++--- lib/metrics.js | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/frontend/static/js/admin/dashboard.js b/frontend/static/js/admin/dashboard.js index de52cf82..736cf637 100644 --- a/frontend/static/js/admin/dashboard.js +++ b/frontend/static/js/admin/dashboard.js @@ -42,17 +42,20 @@ class Dashboard extends LitElement { this.requestUpdate() this.metrics.day = await session.api.server.countMultipleMetricsEvents({ timespan: 'day', - events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'] + events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'], + uniqueBys: {'logged-in': 'user'} }) this.requestUpdate() this.metrics.week = await session.api.server.countMultipleMetricsEvents({ timespan: 'week', - events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'] + events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'], + uniqueBys: {'logged-in': 'user'} }) this.requestUpdate() this.metrics.month = await session.api.server.countMultipleMetricsEvents({ timespan: 'month', - events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'] + events: ['signed-up', 'logged-in', 'community-created', 'post-created', 'comment-created'], + uniqueBys: {'logged-in': 'user'} }) this.requestUpdate() this.httpHits = Object.entries(await session.api.server.aggregateHttpHits({timespan: 'day'})) diff --git a/lib/metrics.js b/lib/metrics.js index 6fe5708a..2ad1baf4 100644 --- a/lib/metrics.js +++ b/lib/metrics.js @@ -34,12 +34,17 @@ export function commentCreated ({user}) { return metricEventsLog.append({event: 'comment-created', user}) } -export async function listEvents ({event, timespan}) { +export async function listEvents ({event, timespan, uniqueBy}) { const log = event === 'http-request' ? trafficLog : metricEventsLog let startTs = timespanToTS(timespan) + let hasSeen = uniqueBy ? new Set() : undefined return log.query(entry => { if (entry.ts < startTs) return false if (event && entry.event !== event) return false + if (uniqueBy) { + if (hasSeen.has(entry[uniqueBy])) return false + hasSeen.add(entry[uniqueBy]) + } return true }) } @@ -48,16 +53,27 @@ export async function countEvents (opts) { return (await listEvents(opts))?.length || 0 } -export async function countMultipleEvents ({events, timespan}) { +export async function countMultipleEvents ({events, timespan, uniqueBys}) { if (!events || !Array.isArray(events)) { throw new Error('Events must be an array of strings') } let startTs = timespanToTS(timespan) const counts = {} events.forEach(evt => counts[evt] = 0) + let hasSeens = undefined + if (uniqueBys) { + hasSeens = {} + for (let k in uniqueBys) { + hasSeens[k] = new Set() + } + } await metricEventsLog.query(entry => { if (entry.ts < startTs) return false if (!events.includes(entry.event)) return false + if (uniqueBys?.[entry.event]) { + if (hasSeens[entry.event].has(entry[uniqueBys[entry.event]])) return false + hasSeens[entry.event].add(entry[uniqueBys[entry.event]]) + } counts[entry.event]++ return false })