Add additional stats to the Stats page (#3812)

* Add o_counter, play_duration, play_count, unique_play_count stats
This commit is contained in:
hontheinternet 2023-07-11 13:32:42 +09:00 committed by GitHub
parent 4f0e0e1d99
commit ff22577ce0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 209 additions and 21 deletions

2
.gitignore vendored
View file

@ -63,4 +63,4 @@ node_modules
/stash /stash
dist dist
.DS_Store .DS_Store
/.local /.local*

View file

@ -40,16 +40,20 @@ query AllTagsForFilter {
query Stats { query Stats {
stats { stats {
scene_count, scene_count
scenes_size, scenes_size
scenes_duration, scenes_duration
image_count, image_count
images_size, images_size
gallery_count, gallery_count
performer_count, performer_count
studio_count, studio_count
movie_count, movie_count
tag_count tag_count
total_o_count
total_play_duration
total_play_count
scenes_played
} }
} }

View file

@ -9,4 +9,8 @@ type StatsResultType {
studio_count: Int! studio_count: Int!
movie_count: Int! movie_count: Int!
tag_count: Int! tag_count: Int!
total_o_count: Int!
total_play_duration: Float!
total_play_count: Int!
scenes_played: Int!
} }

View file

@ -157,18 +157,26 @@ func (r *queryResolver) Stats(ctx context.Context) (*StatsResultType, error) {
studiosCount, _ := studiosQB.Count(ctx) studiosCount, _ := studiosQB.Count(ctx)
moviesCount, _ := moviesQB.Count(ctx) moviesCount, _ := moviesQB.Count(ctx)
tagsCount, _ := tagsQB.Count(ctx) tagsCount, _ := tagsQB.Count(ctx)
totalOCount, _ := scenesQB.OCount(ctx)
totalPlayDuration, _ := scenesQB.PlayDuration(ctx)
totalPlayCount, _ := scenesQB.PlayCount(ctx)
uniqueScenePlayCount, _ := scenesQB.UniqueScenePlayCount(ctx)
ret = StatsResultType{ ret = StatsResultType{
SceneCount: scenesCount, SceneCount: scenesCount,
ScenesSize: scenesSize, ScenesSize: scenesSize,
ScenesDuration: scenesDuration, ScenesDuration: scenesDuration,
ImageCount: imageCount, ImageCount: imageCount,
ImagesSize: imageSize, ImagesSize: imageSize,
GalleryCount: galleryCount, GalleryCount: galleryCount,
PerformerCount: performersCount, PerformerCount: performersCount,
StudioCount: studiosCount, StudioCount: studiosCount,
MovieCount: moviesCount, MovieCount: moviesCount,
TagCount: tagsCount, TagCount: tagsCount,
TotalOCount: totalOCount,
TotalPlayDuration: totalPlayDuration,
TotalPlayCount: totalPlayCount,
ScenesPlayed: uniqueScenePlayCount,
} }
return nil return nil

View file

@ -687,6 +687,27 @@ func (_m *SceneReaderWriter) IncrementWatchCount(ctx context.Context, id int) (i
return r0, r1 return r0, r1
} }
// OCount provides a mock function with given fields: ctx
func (_m *SceneReaderWriter) OCount(ctx context.Context) (int, error) {
ret := _m.Called(ctx)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context) int); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OCountByPerformerID provides a mock function with given fields: ctx, performerID // OCountByPerformerID provides a mock function with given fields: ctx, performerID
func (_m *SceneReaderWriter) OCountByPerformerID(ctx context.Context, performerID int) (int, error) { func (_m *SceneReaderWriter) OCountByPerformerID(ctx context.Context, performerID int) (int, error) {
ret := _m.Called(ctx, performerID) ret := _m.Called(ctx, performerID)
@ -708,6 +729,48 @@ func (_m *SceneReaderWriter) OCountByPerformerID(ctx context.Context, performerI
return r0, r1 return r0, r1
} }
// PlayCount provides a mock function with given fields: ctx
func (_m *SceneReaderWriter) PlayCount(ctx context.Context) (int, error) {
ret := _m.Called(ctx)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context) int); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PlayDuration provides a mock function with given fields: ctx
func (_m *SceneReaderWriter) PlayDuration(ctx context.Context) (float64, error) {
ret := _m.Called(ctx)
var r0 float64
if rf, ok := ret.Get(0).(func(context.Context) float64); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(float64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Query provides a mock function with given fields: ctx, options // Query provides a mock function with given fields: ctx, options
func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) { func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) {
ret := _m.Called(ctx, options) ret := _m.Called(ctx, options)
@ -815,6 +878,27 @@ func (_m *SceneReaderWriter) Size(ctx context.Context) (float64, error) {
return r0, r1 return r0, r1
} }
// UniqueScenePlayCount provides a mock function with given fields: ctx
func (_m *SceneReaderWriter) UniqueScenePlayCount(ctx context.Context) (int, error) {
ret := _m.Called(ctx)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context) int); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: ctx, updatedScene // Update provides a mock function with given fields: ctx, updatedScene
func (_m *SceneReaderWriter) Update(ctx context.Context, updatedScene *models.Scene) error { func (_m *SceneReaderWriter) Update(ctx context.Context, updatedScene *models.Scene) error {
ret := _m.Called(ctx, updatedScene) ret := _m.Called(ctx, updatedScene)

View file

@ -168,12 +168,16 @@ type SceneReader interface {
CountByPerformerID(ctx context.Context, performerID int) (int, error) CountByPerformerID(ctx context.Context, performerID int) (int, error)
OCountByPerformerID(ctx context.Context, performerID int) (int, error) OCountByPerformerID(ctx context.Context, performerID int) (int, error)
OCount(ctx context.Context) (int, error)
// FindByStudioID(studioID int) ([]*Scene, error) // FindByStudioID(studioID int) ([]*Scene, error)
FindByMovieID(ctx context.Context, movieID int) ([]*Scene, error) FindByMovieID(ctx context.Context, movieID int) ([]*Scene, error)
CountByMovieID(ctx context.Context, movieID int) (int, error) CountByMovieID(ctx context.Context, movieID int) (int, error)
Count(ctx context.Context) (int, error) Count(ctx context.Context) (int, error)
PlayCount(ctx context.Context) (int, error)
UniqueScenePlayCount(ctx context.Context) (int, error)
Size(ctx context.Context) (float64, error) Size(ctx context.Context) (float64, error)
Duration(ctx context.Context) (float64, error) Duration(ctx context.Context) (float64, error)
PlayDuration(ctx context.Context) (float64, error)
// SizeCount() (string, error) // SizeCount() (string, error)
CountByStudioID(ctx context.Context, studioID int) (int, error) CountByStudioID(ctx context.Context, studioID int) (int, error)
CountByTagID(ctx context.Context, tagID int) (int, error) CountByTagID(ctx context.Context, tagID int) (int, error)

View file

@ -705,6 +705,18 @@ func (qb *SceneStore) OCountByPerformerID(ctx context.Context, performerID int)
return ret, nil return ret, nil
} }
func (qb *SceneStore) OCount(ctx context.Context) (int, error) {
table := qb.table()
q := dialect.Select(goqu.COALESCE(goqu.SUM("o_counter"), 0)).From(table)
var ret int
if err := querySimple(ctx, q, &ret); err != nil {
return 0, err
}
return ret, nil
}
func (qb *SceneStore) FindByMovieID(ctx context.Context, movieID int) ([]*models.Scene, error) { func (qb *SceneStore) FindByMovieID(ctx context.Context, movieID int) ([]*models.Scene, error) {
sq := dialect.From(scenesMoviesJoinTable).Select(scenesMoviesJoinTable.Col(sceneIDColumn)).Where( sq := dialect.From(scenesMoviesJoinTable).Select(scenesMoviesJoinTable.Col(sceneIDColumn)).Where(
scenesMoviesJoinTable.Col(movieIDColumn).Eq(movieID), scenesMoviesJoinTable.Col(movieIDColumn).Eq(movieID),
@ -730,6 +742,24 @@ func (qb *SceneStore) Count(ctx context.Context) (int, error) {
return count(ctx, q) return count(ctx, q)
} }
func (qb *SceneStore) PlayCount(ctx context.Context) (int, error) {
q := dialect.Select(goqu.COALESCE(goqu.SUM("play_count"), 0)).From(qb.table())
var ret int
if err := querySimple(ctx, q, &ret); err != nil {
return 0, err
}
return ret, nil
}
func (qb *SceneStore) UniqueScenePlayCount(ctx context.Context) (int, error) {
table := qb.table()
q := dialect.Select(goqu.COUNT("*")).From(table).Where(table.Col("play_count").Gt(0))
return count(ctx, q)
}
func (qb *SceneStore) Size(ctx context.Context) (float64, error) { func (qb *SceneStore) Size(ctx context.Context) (float64, error) {
table := qb.table() table := qb.table()
fileTable := fileTableMgr.table fileTable := fileTableMgr.table
@ -771,6 +801,19 @@ func (qb *SceneStore) Duration(ctx context.Context) (float64, error) {
return ret, nil return ret, nil
} }
func (qb *SceneStore) PlayDuration(ctx context.Context) (float64, error) {
table := qb.table()
q := dialect.Select(goqu.COALESCE(goqu.SUM("play_duration"), 0)).From(table)
var ret float64
if err := querySimple(ctx, q, &ret); err != nil {
return 0, err
}
return ret, nil
}
func (qb *SceneStore) CountByStudioID(ctx context.Context, studioID int) (int, error) { func (qb *SceneStore) CountByStudioID(ctx context.Context, studioID int) (int, error) {
table := qb.table() table := qb.table()

View file

@ -18,6 +18,11 @@ export const Stats: React.FC = () => {
3 3
); );
const totalPlayDuration = TextUtils.secondsAsTimeString(
data.stats.total_play_duration,
3
);
return ( return (
<div className="mt-5"> <div className="mt-5">
<div className="col col-sm-8 m-sm-auto row stats"> <div className="col col-sm-8 m-sm-auto row stats">
@ -114,6 +119,38 @@ export const Stats: React.FC = () => {
</p> </p>
</div> </div>
</div> </div>
<div className="col col-sm-8 m-sm-auto row stats">
<div className="stats-element">
<p className="title">
<FormattedNumber value={data.stats.total_o_count} />
</p>
<p className="heading">
<FormattedMessage id="stats.total_o_count" />
</p>
</div>
<div className="stats-element">
<p className="title">
<FormattedNumber value={data.stats.total_play_count} />
</p>
<p className="heading">
<FormattedMessage id="stats.total_play_count" />
</p>
</div>
<div className="stats-element">
<p className="title">
<FormattedNumber value={data.stats.scenes_played} />
</p>
<p className="heading">
<FormattedMessage id="stats.scenes_played" />
</p>
</div>
<div className="stats-element">
<p className="title">{totalPlayDuration || "-"}</p>
<p className="heading">
<FormattedMessage id="stats.total_play_duration" />
</p>
</div>
</div>
</div> </div>
); );
}; };

View file

@ -1218,7 +1218,11 @@
"stats": { "stats": {
"image_size": "Images size", "image_size": "Images size",
"scenes_duration": "Scenes duration", "scenes_duration": "Scenes duration",
"scenes_size": "Scenes size" "scenes_size": "Scenes size",
"scenes_played": "Scenes Played",
"total_play_duration": "Total Play Duration",
"total_play_count": "Total Play Count",
"total_o_count": "Total O-Count"
}, },
"status": "Status: {statusText}", "status": "Status: {statusText}",
"studio": "Studio", "studio": "Studio",