mirror of https://github.com/Chocobozzz/PeerTube
Use inner join and document code for viewr stats for channels
parent
714bfcc556
commit
3d527ba173
|
@ -56,6 +56,7 @@
|
|||
"@ngx-loading-bar/router": "^4.2.0",
|
||||
"@ngx-meta/core": "^8.0.2",
|
||||
"@ngx-translate/i18n-polyfill": "^1.0.0",
|
||||
"@types/chart.js": "^2.9.16",
|
||||
"@types/core-js": "^2.5.2",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/hls.js": "^0.12.4",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<div i18n class="video-channel-followers">{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
|
||||
|
||||
<div *ngIf="!isInSmallView" class="w-100 d-flex justify-content-end">
|
||||
<p-chart *ngIf="videoChannelsData && videoChannelsData[i]" type="line" [data]="videoChannelsData[i]" [options]="chartOptions" width="40vw" height="100px"></p-chart>
|
||||
<p-chart *ngIf="videoChannelsChartData && videoChannelsChartData[i]" type="line" [data]="videoChannelsChartData[i]" [options]="chartOptions" width="40vw" height="100px"></p-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ import { ScreenService } from '@app/shared/misc/screen.service'
|
|||
import { User } from '@app/shared'
|
||||
import { flatMap } from 'rxjs/operators'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { minBy, maxBy } from 'lodash-es'
|
||||
import { min, minBy, max, maxBy } from 'lodash-es'
|
||||
import { ChartData } from 'chart.js'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-video-channels',
|
||||
|
@ -17,7 +18,7 @@ import { minBy, maxBy } from 'lodash-es'
|
|||
})
|
||||
export class MyAccountVideoChannelsComponent implements OnInit {
|
||||
videoChannels: VideoChannel[] = []
|
||||
videoChannelsData: any[]
|
||||
videoChannelsChartData: ChartData[]
|
||||
videoChannelsMinimumDailyViews = 0
|
||||
videoChannelsMaximumDailyViews: number
|
||||
|
||||
|
@ -125,7 +126,9 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
|||
.pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true)))
|
||||
.subscribe(res => {
|
||||
this.videoChannels = res.data
|
||||
this.videoChannelsData = this.videoChannels.map(v => ({
|
||||
|
||||
// chart data
|
||||
this.videoChannelsChartData = this.videoChannels.map(v => ({
|
||||
labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()),
|
||||
datasets: [
|
||||
{
|
||||
|
@ -135,9 +138,22 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
|||
borderColor: "#c6c6c6"
|
||||
}
|
||||
]
|
||||
}))
|
||||
this.videoChannelsMinimumDailyViews = minBy(this.videoChannels.map(v => minBy(v.viewsPerDay, day => day.views)), day => day.views).views
|
||||
this.videoChannelsMaximumDailyViews = maxBy(this.videoChannels.map(v => maxBy(v.viewsPerDay, day => day.views)), day => day.views).views
|
||||
} as ChartData))
|
||||
|
||||
// chart options that depend on chart data:
|
||||
// we don't want to skew values and have min at 0, so we define what the floor/ceiling is here
|
||||
this.videoChannelsMinimumDailyViews = min(
|
||||
this.videoChannels.map(v => minBy( // compute local minimum daily views for each channel, by their "views" attribute
|
||||
v.viewsPerDay,
|
||||
day => day.views
|
||||
).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
|
||||
)
|
||||
this.videoChannelsMaximumDailyViews = max(
|
||||
this.videoChannels.map(v => maxBy( // compute local maximum daily views for each channel, by their "views" attribute
|
||||
v.viewsPerDay,
|
||||
day => day.views
|
||||
).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { VideoChannel as ServerVideoChannel, viewsPerTime } from '../../../../../shared/models/videos'
|
||||
import { VideoChannel as ServerVideoChannel, ViewsPerDate } from '../../../../../shared/models/videos'
|
||||
import { Actor } from '../actor/actor.model'
|
||||
import { Account } from '../../../../../shared/models/actors'
|
||||
|
||||
|
@ -12,7 +12,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
|
|||
ownerAccount?: Account
|
||||
ownerBy?: string
|
||||
ownerAvatarUrl?: string
|
||||
viewsPerDay?: viewsPerTime[]
|
||||
viewsPerDay?: ViewsPerDate[]
|
||||
|
||||
constructor (hash: ServerVideoChannel) {
|
||||
super(hash)
|
||||
|
|
|
@ -1149,6 +1149,13 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/chart.js@^2.9.16":
|
||||
version "2.9.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.16.tgz#ac9d268fa192c0ec0efd740f802683e3ed97642c"
|
||||
integrity sha512-Mofg7xFIeAWME46YMVKHPCyUz2Z0KsVMNE1f4oF3T74mK3RiPQxOm9qzoeNTyMs6lpl4x0tiHL+Wsz2DHCxQlQ==
|
||||
dependencies:
|
||||
moment "^2.10.2"
|
||||
|
||||
"@types/core-js@^2.5.2":
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.2.tgz#d4c25420044d4a5b65e00a82fc04b7824b62691f"
|
||||
|
|
|
@ -166,7 +166,10 @@ export type SummaryOptions = {
|
|||
VideoModel
|
||||
]
|
||||
},
|
||||
[ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => ({
|
||||
[ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
|
||||
const daysPrior = parseInt(options.daysPrior + '', 10)
|
||||
|
||||
return {
|
||||
attributes: {
|
||||
include: [
|
||||
[
|
||||
|
@ -176,23 +179,20 @@ export type SummaryOptions = {
|
|||
'FROM ( ' +
|
||||
'WITH ' +
|
||||
'days AS ( ' +
|
||||
`SELECT generate_series(date_trunc('day', now()) - '${options.daysPrior} day'::interval, ` +
|
||||
`SELECT generate_series(date_trunc('day', now()) - '${daysPrior} day'::interval, ` +
|
||||
`date_trunc('day', now()), '1 day'::interval) AS day ` +
|
||||
'), ' +
|
||||
'views AS ( ' +
|
||||
'SELECT * ' +
|
||||
'FROM "videoView" ' +
|
||||
'WHERE "videoView"."videoId" IN ( ' +
|
||||
'SELECT "video"."id" ' +
|
||||
'FROM "video" ' +
|
||||
'SELECT v.* ' +
|
||||
'FROM "videoView" AS v ' +
|
||||
'INNER JOIN "video" ON "video"."id" = v."videoId" ' +
|
||||
'WHERE "video"."channelId" = "VideoChannelModel"."id" ' +
|
||||
') ' +
|
||||
') ' +
|
||||
'SELECT days.day AS day, ' +
|
||||
'COALESCE(SUM(views.views), 0) AS views ' +
|
||||
'FROM days ' +
|
||||
`LEFT JOIN views ON date_trunc('day', "views"."startDate") = date_trunc('day', days.day) ` +
|
||||
'GROUP BY 1 ' +
|
||||
'GROUP BY day ' +
|
||||
'ORDER BY day ' +
|
||||
') t' +
|
||||
')'
|
||||
|
@ -201,7 +201,8 @@ export type SummaryOptions = {
|
|||
]
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
@Table({
|
||||
tableName: 'videoChannel',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import { User, Video, VideoChannel, viewsPerTime, VideoDetails } from '../../../../shared/index'
|
||||
import { User, Video, VideoChannel, ViewsPerDate, VideoDetails } from '../../../../shared/index'
|
||||
import {
|
||||
cleanupTests,
|
||||
createUser,
|
||||
|
@ -376,7 +376,7 @@ describe('Test video channels', function () {
|
|||
res.body.data.forEach((channel: VideoChannel) => {
|
||||
expect(channel).to.haveOwnProperty('viewsPerDay')
|
||||
expect(channel.viewsPerDay).to.have.length(30 + 1) // daysPrior + today
|
||||
channel.viewsPerDay.forEach((v: viewsPerTime) => {
|
||||
channel.viewsPerDay.forEach((v: ViewsPerDate) => {
|
||||
expect(v.date).to.be.an('string')
|
||||
expect(v.views).to.equal(0)
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Actor } from '../../actors/actor.model'
|
|||
import { Account } from '../../actors/index'
|
||||
import { Avatar } from '../../avatars'
|
||||
|
||||
export type viewsPerTime = {
|
||||
export type ViewsPerDate = {
|
||||
date: Date
|
||||
views: number
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export interface VideoChannel extends Actor {
|
|||
support: string
|
||||
isLocal: boolean
|
||||
ownerAccount?: Account
|
||||
viewsPerDay?: viewsPerTime[] // chronologically ordered
|
||||
viewsPerDay?: ViewsPerDate[] // chronologically ordered
|
||||
}
|
||||
|
||||
export interface VideoChannelSummary {
|
||||
|
|
Loading…
Reference in New Issue