Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Framasoft
PeerTube
PeerTube
Commits
49f0468d
Verified
Commit
49f0468d
authored
May 05, 2022
by
Chocobozzz
Browse files
Add filter by start/end date overall stats in api
parent
f18a060a
Changes
9
Hide whitespace changes
Inline
Side-by-side
server/controllers/api/videos/stats.ts
View file @
49f0468d
import
express
from
'
express
'
import
{
LocalVideoViewerModel
}
from
'
@server/models/view/local-video-viewer
'
import
{
VideoStatsTimeserieMetric
,
VideoStatsTimeserieQuery
}
from
'
@shared/models
'
import
{
VideoStatsOverallQuery
,
VideoStatsTimeserieMetric
,
VideoStatsTimeserieQuery
}
from
'
@shared/models
'
import
{
asyncMiddleware
,
authenticate
,
...
...
@@ -39,8 +39,13 @@ export {
async
function
getOverallStats
(
req
:
express
.
Request
,
res
:
express
.
Response
)
{
const
video
=
res
.
locals
.
videoAll
const
query
=
req
.
query
as
VideoStatsOverallQuery
const
stats
=
await
LocalVideoViewerModel
.
getOverallStats
(
video
)
const
stats
=
await
LocalVideoViewerModel
.
getOverallStats
({
video
,
startDate
:
query
.
startDate
,
endDate
:
query
.
endDate
})
return
res
.
json
(
stats
)
}
...
...
server/middlewares/validators/videos/video-stats.ts
View file @
49f0468d
...
...
@@ -10,6 +10,16 @@ import { areValidationErrors, checkUserCanManageVideo, doesVideoExist, isValidVi
const
videoOverallStatsValidator
=
[
isValidVideoIdParam
(
'
videoId
'
),
query
(
'
startDate
'
)
.
optional
()
.
custom
(
isDateValid
)
.
withMessage
(
'
Should have a valid start date
'
),
query
(
'
endDate
'
)
.
optional
()
.
custom
(
isDateValid
)
.
withMessage
(
'
Should have a valid end date
'
),
async
(
req
:
express
.
Request
,
res
:
express
.
Response
,
next
:
express
.
NextFunction
)
=>
{
logger
.
debug
(
'
Checking videoOverallStatsValidator parameters
'
,
{
parameters
:
req
.
body
})
...
...
server/models/view/local-video-viewer.ts
View file @
49f0468d
...
...
@@ -100,10 +100,28 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
})
}
static
async
getOverallStats
(
video
:
MVideo
):
Promise
<
VideoStatsOverall
>
{
const
options
=
{
static
async
getOverallStats
(
options
:
{
video
:
MVideo
startDate
?:
string
endDate
?:
string
}):
Promise
<
VideoStatsOverall
>
{
const
{
video
,
startDate
,
endDate
}
=
options
const
queryOptions
=
{
type
:
QueryTypes
.
SELECT
as
QueryTypes
.
SELECT
,
replacements
:
{
videoId
:
video
.
id
}
replacements
:
{
videoId
:
video
.
id
}
as
any
}
let
dateWhere
=
''
if
(
startDate
)
{
dateWhere
+=
'
AND "localVideoViewer"."startDate" >= :startDate
'
queryOptions
.
replacements
.
startDate
=
startDate
}
if
(
endDate
)
{
dateWhere
+=
'
AND "localVideoViewer"."endDate" <= :endDate
'
queryOptions
.
replacements
.
endDate
=
endDate
}
const
watchTimeQuery
=
`SELECT `
+
...
...
@@ -111,9 +129,9 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
`AVG("localVideoViewer"."watchTime") AS "averageWatchTime" `
+
`FROM "localVideoViewer" `
+
`INNER JOIN "video" ON "video"."id" = "localVideoViewer"."videoId" `
+
`WHERE "videoId" = :videoId`
`WHERE "videoId" = :videoId
${
dateWhere
}
`
const
watchTimePromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
watchTimeQuery
,
o
ptions
)
const
watchTimePromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
watchTimeQuery
,
queryO
ptions
)
const
watchPeakQuery
=
`WITH "watchPeakValues" AS (
SELECT "startDate" AS "dateBreakpoint", 1 AS "inc"
...
...
@@ -122,7 +140,7 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
UNION ALL
SELECT "endDate" AS "dateBreakpoint", -1 AS "inc"
FROM "localVideoViewer"
WHERE "videoId" = :videoId
WHERE "videoId" = :videoId
${
dateWhere
}
)
SELECT "dateBreakpoint", "concurrent"
FROM (
...
...
@@ -132,14 +150,14 @@ export class LocalVideoViewerModel extends Model<Partial<AttributesOnly<LocalVid
) tmp
ORDER BY "concurrent" DESC
FETCH FIRST 1 ROW ONLY`
const
watchPeakPromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
watchPeakQuery
,
o
ptions
)
const
watchPeakPromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
watchPeakQuery
,
queryO
ptions
)
const
countriesQuery
=
`SELECT country, COUNT(country) as viewers `
+
`FROM "localVideoViewer" `
+
`WHERE "videoId" = :videoId AND country IS NOT NULL `
+
`WHERE "videoId" = :videoId AND country IS NOT NULL
${
dateWhere
}
`
+
`GROUP BY country `
+
`ORDER BY viewers DESC`
const
countriesPromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
countriesQuery
,
o
ptions
)
const
countriesPromise
=
LocalVideoViewerModel
.
sequelize
.
query
<
any
>
(
countriesQuery
,
queryO
ptions
)
const
[
rowsWatchTime
,
rowsWatchPeak
,
rowsCountries
]
=
await
Promise
.
all
([
watchTimePromise
,
...
...
server/tests/api/check-params/views.ts
View file @
49f0468d
...
...
@@ -75,8 +75,30 @@ describe('Test videos views', function () {
})
})
it
(
'
Should fail with an invalid start date
'
,
async
function
()
{
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
,
startDate
:
'
fake
'
as
any
,
endDate
:
new
Date
().
toISOString
(),
expectedStatus
:
HttpStatusCode
.
BAD_REQUEST_400
})
})
it
(
'
Should fail with an invalid end date
'
,
async
function
()
{
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
,
startDate
:
new
Date
().
toISOString
(),
endDate
:
'
fake
'
as
any
,
expectedStatus
:
HttpStatusCode
.
BAD_REQUEST_400
})
})
it
(
'
Should succeed with the correct parameters
'
,
async
function
()
{
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
})
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
,
startDate
:
new
Date
().
toISOString
(),
endDate
:
new
Date
().
toISOString
()
})
})
})
...
...
server/tests/api/views/video-views-overall-stats.ts
View file @
49f0468d
...
...
@@ -141,6 +141,27 @@ describe('Test views overall stats', function () {
}
})
it
(
'
Should filter overall stats by date
'
,
async
function
()
{
this
.
timeout
(
60000
)
const
beforeView
=
new
Date
()
await
servers
[
0
].
views
.
simulateViewer
({
id
:
vodVideoId
,
currentTimes
:
[
0
,
3
]
})
await
processViewersStats
(
servers
)
{
const
stats
=
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
:
vodVideoId
,
startDate
:
beforeView
.
toISOString
()
})
expect
(
stats
.
averageWatchTime
).
to
.
equal
(
3
)
expect
(
stats
.
totalWatchTime
).
to
.
equal
(
3
)
}
{
const
stats
=
await
servers
[
0
].
videoStats
.
getOverallStats
({
videoId
:
liveVideoId
,
endDate
:
beforeView
.
toISOString
()
})
expect
(
stats
.
averageWatchTime
).
to
.
equal
(
22
)
expect
(
stats
.
totalWatchTime
).
to
.
equal
(
88
)
}
})
after
(
async
function
()
{
await
stopFfmpeg
(
command
)
})
...
...
shared/models/videos/stats/index.ts
View file @
49f0468d
export
*
from
'
./video-stats-overall-query.model
'
export
*
from
'
./video-stats-overall.model
'
export
*
from
'
./video-stats-retention.model
'
export
*
from
'
./video-stats-timeserie-query.model
'
...
...
shared/models/videos/stats/video-stats-overall-query.model.ts
0 → 100644
View file @
49f0468d
export
interface
VideoStatsOverallQuery
{
startDate
?:
string
endDate
?:
string
}
shared/server-commands/videos/video-stats-command.ts
View file @
49f0468d
...
...
@@ -6,6 +6,8 @@ export class VideoStatsCommand extends AbstractCommand {
getOverallStats
(
options
:
OverrideCommandOptions
&
{
videoId
:
number
|
string
startDate
?:
string
endDate
?:
string
})
{
const
path
=
'
/api/v1/videos/
'
+
options
.
videoId
+
'
/stats/overall
'
...
...
@@ -13,6 +15,8 @@ export class VideoStatsCommand extends AbstractCommand {
...
options
,
path
,
query
:
pick
(
options
,
[
'
startDate
'
,
'
endDate
'
]),
implicitToken
:
true
,
defaultExpectedStatus
:
HttpStatusCode
.
OK_200
})
...
...
support/doc/api/openapi.yaml
View file @
49f0468d
...
...
@@ -1952,6 +1952,18 @@ paths:
-
OAuth2
:
[]
parameters
:
-
$ref
:
'
#/components/parameters/idOrUUID'
-
name
:
startDate
in
:
query
description
:
Filter stats by start date
schema
:
type
:
string
format
:
date-time
-
name
:
endDate
in
:
query
description
:
Filter stats by end date
schema
:
type
:
string
format
:
date-time
responses
:
'
200'
:
description
:
successful operation
...
...
@@ -1996,6 +2008,18 @@ paths:
enum
:
-
'
viewers'
-
'
aggregateWatchTime'
-
name
:
startDate
in
:
query
description
:
Filter stats by start date
schema
:
type
:
string
format
:
date-time
-
name
:
endDate
in
:
query
description
:
Filter stats by end date
schema
:
type
:
string
format
:
date-time
responses
:
'
200'
:
description
:
successful operation
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment