aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-04-08 10:22:56 +0200
committerChocobozzz <chocobozzz@cpy.re>2022-04-15 09:49:35 +0200
commit3eda9b775ae700ac544e8c5588514627796b83cd (patch)
tree97ec1fdfce274e83d976352f5b4154c315ee33d7 /server
parent901bcf5c188ea79350fecd499ad76460b866617b (diff)
downloadPeerTube-3eda9b775ae700ac544e8c5588514627796b83cd.tar.gz
PeerTube-3eda9b775ae700ac544e8c5588514627796b83cd.tar.zst
PeerTube-3eda9b775ae700ac544e8c5588514627796b83cd.zip
Support interactive video stats graph
Diffstat (limited to 'server')
-rw-r--r--server/lib/timeserie.ts24
-rw-r--r--server/models/view/local-video-viewer.ts8
-rw-r--r--server/tests/api/views/video-views-timeserie-stats.ts22
3 files changed, 24 insertions, 30 deletions
diff --git a/server/lib/timeserie.ts b/server/lib/timeserie.ts
index d8f700a2f..bd3d1c1ca 100644
--- a/server/lib/timeserie.ts
+++ b/server/lib/timeserie.ts
@@ -1,24 +1,17 @@
1import { logger } from '@server/helpers/logger' 1import { logger } from '@server/helpers/logger'
2import { VideoStatsTimeserieGroupInterval } from '@shared/models'
3 2
4function buildGroupByAndBoundaries (startDateString: string, endDateString: string) { 3function buildGroupByAndBoundaries (startDateString: string, endDateString: string) {
5 const startDate = new Date(startDateString) 4 const startDate = new Date(startDateString)
6 const endDate = new Date(endDateString) 5 const endDate = new Date(endDateString)
7 6
8 const groupByMatrix: { [ id in VideoStatsTimeserieGroupInterval ]: string } = {
9 one_day: '1 day',
10 one_hour: '1 hour',
11 ten_minutes: '10 minutes',
12 one_minute: '1 minute'
13 }
14 const groupInterval = buildGroupInterval(startDate, endDate) 7 const groupInterval = buildGroupInterval(startDate, endDate)
15 8
16 logger.debug('Found "%s" group interval.', groupInterval, { startDate, endDate }) 9 logger.debug('Found "%s" group interval.', groupInterval, { startDate, endDate })
17 10
18 // Remove parts of the date we don't need 11 // Remove parts of the date we don't need
19 if (groupInterval === 'one_day') { 12 if (groupInterval.endsWith(' day') || groupInterval.endsWith(' days')) {
20 startDate.setHours(0, 0, 0, 0) 13 startDate.setHours(0, 0, 0, 0)
21 } else if (groupInterval === 'one_hour') { 14 } else if (groupInterval.endsWith(' hour') || groupInterval.endsWith(' hours')) {
22 startDate.setMinutes(0, 0, 0) 15 startDate.setMinutes(0, 0, 0)
23 } else { 16 } else {
24 startDate.setSeconds(0, 0) 17 startDate.setSeconds(0, 0)
@@ -26,7 +19,6 @@ function buildGroupByAndBoundaries (startDateString: string, endDateString: stri
26 19
27 return { 20 return {
28 groupInterval, 21 groupInterval,
29 sqlInterval: groupByMatrix[groupInterval],
30 startDate, 22 startDate,
31 endDate 23 endDate
32 } 24 }
@@ -40,16 +32,18 @@ export {
40 32
41// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
42 34
43function buildGroupInterval (startDate: Date, endDate: Date): VideoStatsTimeserieGroupInterval { 35function buildGroupInterval (startDate: Date, endDate: Date): string {
44 const aDay = 86400 36 const aDay = 86400
45 const anHour = 3600 37 const anHour = 3600
46 const aMinute = 60 38 const aMinute = 60
47 39
48 const diffSeconds = (endDate.getTime() - startDate.getTime()) / 1000 40 const diffSeconds = (endDate.getTime() - startDate.getTime()) / 1000
49 41
50 if (diffSeconds >= 6 * aDay) return 'one_day' 42 if (diffSeconds >= 15 * aDay) return '1 day'
51 if (diffSeconds >= 6 * anHour) return 'one_hour' 43 if (diffSeconds >= 8 * aDay) return '12 hours'
52 if (diffSeconds >= 60 * aMinute) return 'ten_minutes' 44 if (diffSeconds >= 4 * aDay) return '6 hours'
45 if (diffSeconds >= 15 * anHour) return '1 hour'
46 if (diffSeconds >= 180 * aMinute) return '10 minutes'
53 47
54 return 'one_minute' 48 return '1 minute'
55} 49}
diff --git a/server/models/view/local-video-viewer.ts b/server/models/view/local-video-viewer.ts
index ad2ad35ca..b6ddcbb57 100644
--- a/server/models/view/local-video-viewer.ts
+++ b/server/models/view/local-video-viewer.ts
@@ -221,7 +221,7 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
221 }): Promise<VideoStatsTimeserie> { 221 }): Promise<VideoStatsTimeserie> {
222 const { video, metric } = options 222 const { video, metric } = options
223 223
224 const { groupInterval, sqlInterval, startDate, endDate } = buildGroupByAndBoundaries(options.startDate, options.endDate) 224 const { groupInterval, startDate, endDate } = buildGroupByAndBoundaries(options.startDate, options.endDate)
225 225
226 const selectMetrics: { [ id in VideoStatsTimeserieMetric ]: string } = { 226 const selectMetrics: { [ id in VideoStatsTimeserieMetric ]: string } = {
227 viewers: 'COUNT("localVideoViewer"."id")', 227 viewers: 'COUNT("localVideoViewer"."id")',
@@ -230,9 +230,9 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
230 230
231 const query = `WITH "intervals" AS ( 231 const query = `WITH "intervals" AS (
232 SELECT 232 SELECT
233 "time" AS "startDate", "time" + :sqlInterval::interval as "endDate" 233 "time" AS "startDate", "time" + :groupInterval::interval as "endDate"
234 FROM 234 FROM
235 generate_series(:startDate::timestamptz, :endDate::timestamptz, :sqlInterval::interval) serie("time") 235 generate_series(:startDate::timestamptz, :endDate::timestamptz, :groupInterval::interval) serie("time")
236 ) 236 )
237 SELECT "intervals"."startDate" as "date", COALESCE(${selectMetrics[metric]}, 0) AS value 237 SELECT "intervals"."startDate" as "date", COALESCE(${selectMetrics[metric]}, 0) AS value
238 FROM 238 FROM
@@ -249,7 +249,7 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
249 replacements: { 249 replacements: {
250 startDate, 250 startDate,
251 endDate, 251 endDate,
252 sqlInterval, 252 groupInterval,
253 videoId: video.id 253 videoId: video.id
254 } 254 }
255 } 255 }
diff --git a/server/tests/api/views/video-views-timeserie-stats.ts b/server/tests/api/views/video-views-timeserie-stats.ts
index 4db76fe89..fd3aba188 100644
--- a/server/tests/api/views/video-views-timeserie-stats.ts
+++ b/server/tests/api/views/video-views-timeserie-stats.ts
@@ -110,21 +110,21 @@ describe('Test views timeserie stats', function () {
110 110
111 it('Should use a custom start/end date', async function () { 111 it('Should use a custom start/end date', async function () {
112 const now = new Date() 112 const now = new Date()
113 const tenDaysAgo = new Date() 113 const twentyDaysAgo = new Date()
114 tenDaysAgo.setDate(tenDaysAgo.getDate() - 9) 114 twentyDaysAgo.setDate(twentyDaysAgo.getDate() - 19)
115 115
116 const result = await servers[0].videoStats.getTimeserieStats({ 116 const result = await servers[0].videoStats.getTimeserieStats({
117 videoId: vodVideoId, 117 videoId: vodVideoId,
118 metric: 'aggregateWatchTime', 118 metric: 'aggregateWatchTime',
119 startDate: tenDaysAgo, 119 startDate: twentyDaysAgo,
120 endDate: now 120 endDate: now
121 }) 121 })
122 122
123 expect(result.groupInterval).to.equal('one_day') 123 expect(result.groupInterval).to.equal('1 day')
124 expect(result.data).to.have.lengthOf(10) 124 expect(result.data).to.have.lengthOf(20)
125 125
126 const first = result.data[0] 126 const first = result.data[0]
127 expect(new Date(first.date).toLocaleDateString()).to.equal(tenDaysAgo.toLocaleDateString()) 127 expect(new Date(first.date).toLocaleDateString()).to.equal(twentyDaysAgo.toLocaleDateString())
128 128
129 expectInterval(result, 24 * 3600 * 1000) 129 expectInterval(result, 24 * 3600 * 1000)
130 expectTodayLastValue(result, 9) 130 expectTodayLastValue(result, 9)
@@ -142,7 +142,7 @@ describe('Test views timeserie stats', function () {
142 endDate: now 142 endDate: now
143 }) 143 })
144 144
145 expect(result.groupInterval).to.equal('one_hour') 145 expect(result.groupInterval).to.equal('1 hour')
146 expect(result.data).to.have.length.above(24).and.below(50) 146 expect(result.data).to.have.length.above(24).and.below(50)
147 147
148 expectInterval(result, 3600 * 1000) 148 expectInterval(result, 3600 * 1000)
@@ -152,7 +152,7 @@ describe('Test views timeserie stats', function () {
152 it('Should automatically group by ten minutes', async function () { 152 it('Should automatically group by ten minutes', async function () {
153 const now = new Date() 153 const now = new Date()
154 const twoHoursAgo = new Date() 154 const twoHoursAgo = new Date()
155 twoHoursAgo.setHours(twoHoursAgo.getHours() - 1) 155 twoHoursAgo.setHours(twoHoursAgo.getHours() - 4)
156 156
157 const result = await servers[0].videoStats.getTimeserieStats({ 157 const result = await servers[0].videoStats.getTimeserieStats({
158 videoId: vodVideoId, 158 videoId: vodVideoId,
@@ -161,8 +161,8 @@ describe('Test views timeserie stats', function () {
161 endDate: now 161 endDate: now
162 }) 162 })
163 163
164 expect(result.groupInterval).to.equal('ten_minutes') 164 expect(result.groupInterval).to.equal('10 minutes')
165 expect(result.data).to.have.length.above(6).and.below(18) 165 expect(result.data).to.have.length.above(20).and.below(30)
166 166
167 expectInterval(result, 60 * 10 * 1000) 167 expectInterval(result, 60 * 10 * 1000)
168 expectTodayLastValue(result, 9) 168 expectTodayLastValue(result, 9)
@@ -180,7 +180,7 @@ describe('Test views timeserie stats', function () {
180 endDate: now 180 endDate: now
181 }) 181 })
182 182
183 expect(result.groupInterval).to.equal('one_minute') 183 expect(result.groupInterval).to.equal('1 minute')
184 expect(result.data).to.have.length.above(20).and.below(40) 184 expect(result.data).to.have.length.above(20).and.below(40)
185 185
186 expectInterval(result, 60 * 1000) 186 expectInterval(result, 60 * 1000)