From 3d0a8f653a4c3d7b0ddedb078dd930f487767a12 Mon Sep 17 00:00:00 2001 From: randemgame <61895715+randemgame@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:32:08 +0200 Subject: [PATCH] Added Sort Performers by Last O At / Last Played At / Play Count and Added Filter Performers by Play Count. Changes to display O Count rather than O-Counter for better consistency. Grammar fixes for 'Interactive Speed' and 'pHash'. (#4649) * Sort Performers by Last O / View Added 2 New Sorts 'Last O At' and 'Last Played At' for Performers * Filter Performers by Play Count Was not sure whether to label this 'views' as the code does, or 'plays' but chose the latter as it gives parity across the scenes and performers filters. * Sort Performers by Play Count Reutilised the prior selectPerformerLastOAtSQL code that was used to filter by play count to additionally provide useful sorting options. * Replaced O-Counter with O Count To better match other sort and filter options like Gallery Count, Image Count, Play Count, Scene Count, Tag Count, File Count, Performer Count and Play Count, we should really use O Count rather than O-Counter for increased legibility and coherence. * Title Case on 'Interactive speed' and correct capitalization for 'phash' Every other filter/sort option is using Title Case other than 'Interactive speed' which stands out as incorrect. Also, fixing the correct mid-word capitalization on phash to pHash. * Formatting Formatted source code and Ran all tests --- graphql/schema/types/filters.graphql | 2 + pkg/models/performer.go | 2 + pkg/sqlite/performer.go | 92 +++++++++++++++++++ .../Performers/PerformerListTable.tsx | 2 +- .../src/components/Scenes/SceneListTable.tsx | 2 +- .../components/Scenes/SceneMergeDialog.tsx | 2 +- ui/v2.5/src/locales/en-GB.json | 9 +- ui/v2.5/src/models/list-filter/images.ts | 18 ++-- ui/v2.5/src/models/list-filter/performers.ts | 8 +- ui/v2.5/src/models/list-filter/scenes.ts | 13 ++- 10 files changed, 128 insertions(+), 22 deletions(-) diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 643029db2..3164a010b 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -143,6 +143,8 @@ input PerformerFilterType { image_count: IntCriterionInput "Filter by gallery count" gallery_count: IntCriterionInput + "Filter by play count" + play_count: IntCriterionInput "Filter by o count" o_counter: IntCriterionInput "Filter by StashID" diff --git a/pkg/models/performer.go b/pkg/models/performer.go index 9449d611d..9f5b1b51f 100644 --- a/pkg/models/performer.go +++ b/pkg/models/performer.go @@ -160,6 +160,8 @@ type PerformerFilterType struct { ImageCount *IntCriterionInput `json:"image_count"` // Filter by gallery count GalleryCount *IntCriterionInput `json:"gallery_count"` + // Filter by play count + PlayCount *IntCriterionInput `json:"play_count"` // Filter by O count OCounter *IntCriterionInput `json:"o_counter"` // Filter by StashID diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index 794c2b6cc..f10706b45 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -670,6 +670,7 @@ func (qb *PerformerStore) makeFilter(ctx context.Context, filter *models.Perform query.handleCriterion(ctx, performerSceneCountCriterionHandler(qb, filter.SceneCount)) query.handleCriterion(ctx, performerImageCountCriterionHandler(qb, filter.ImageCount)) query.handleCriterion(ctx, performerGalleryCountCriterionHandler(qb, filter.GalleryCount)) + query.handleCriterion(ctx, performerPlayCounterCriterionHandler(qb, filter.PlayCount)) query.handleCriterion(ctx, performerOCounterCriterionHandler(qb, filter.OCounter)) query.handleCriterion(ctx, dateCriterionHandler(filter.Birthdate, tableName+".birthdate")) query.handleCriterion(ctx, dateCriterionHandler(filter.DeathDate, tableName+".death_date")) @@ -874,6 +875,63 @@ var selectPerformerOCountSQL = utils.StrFormat( }, ) +// used for sorting and filtering play count on performer view count +var selectPerformerPlayCountSQL = utils.StrFormat( + "SELECT COUNT(DISTINCT {view_date}) FROM ("+ + "SELECT {view_date} FROM {performers_scenes} s "+ + "LEFT JOIN {scenes} ON {scenes}.id = s.{scene_id} "+ + "LEFT JOIN {scenes_view_dates} ON {scenes_view_dates}.{scene_id} = {scenes}.id "+ + "WHERE s.{performer_id} = {performers}.id"+ + ")", + map[string]interface{}{ + "performer_id": performerIDColumn, + "performers": performerTable, + "performers_scenes": performersScenesTable, + "scenes": sceneTable, + "scene_id": sceneIDColumn, + "scenes_view_dates": scenesViewDatesTable, + "view_date": sceneViewDateColumn, + }, +) + +// used for sorting on performer last o_date +var selectPerformerLastOAtSQL = utils.StrFormat( + "SELECT MAX(o_date) FROM ("+ + "SELECT {o_date} FROM {performers_scenes} s "+ + "LEFT JOIN {scenes} ON {scenes}.id = s.{scene_id} "+ + "LEFT JOIN {scenes_o_dates} ON {scenes_o_dates}.{scene_id} = {scenes}.id "+ + "WHERE s.{performer_id} = {performers}.id"+ + ")", + map[string]interface{}{ + "performer_id": performerIDColumn, + "performers": performerTable, + "performers_scenes": performersScenesTable, + "scenes": sceneTable, + "scene_id": sceneIDColumn, + "scenes_o_dates": scenesODatesTable, + "o_date": sceneODateColumn, + }, +) + +// used for sorting on performer last view_date +var selectPerformerLastPlayedAtSQL = utils.StrFormat( + "SELECT MAX(view_date) FROM ("+ + "SELECT {view_date} FROM {performers_scenes} s "+ + "LEFT JOIN {scenes} ON {scenes}.id = s.{scene_id} "+ + "LEFT JOIN {scenes_view_dates} ON {scenes_view_dates}.{scene_id} = {scenes}.id "+ + "WHERE s.{performer_id} = {performers}.id"+ + ")", + map[string]interface{}{ + "performer_id": performerIDColumn, + "performers": performerTable, + "performers_scenes": performersScenesTable, + "scenes": sceneTable, + "scene_id": sceneIDColumn, + "scenes_view_dates": scenesViewDatesTable, + "view_date": sceneViewDateColumn, + }, +) + func performerOCounterCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc { return func(ctx context.Context, f *filterBuilder) { if count == nil { @@ -887,6 +945,19 @@ func performerOCounterCriterionHandler(qb *PerformerStore, count *models.IntCrit } } +func performerPlayCounterCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc { + return func(ctx context.Context, f *filterBuilder) { + if count == nil { + return + } + + lhs := "(" + selectPerformerPlayCountSQL + ")" + clause, args := getIntCriterionWhereClause(lhs, *count) + + f.addWhere(clause, args...) + } +} + func performerStudiosCriterionHandler(qb *PerformerStore, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(ctx context.Context, f *filterBuilder) { if studios != nil { @@ -1027,6 +1098,21 @@ func (qb *PerformerStore) sortByOCounter(direction string) string { return " ORDER BY (" + selectPerformerOCountSQL + ") " + direction } +func (qb *PerformerStore) sortByPlayCount(direction string) string { + // need to sum the o_counter from scenes and images + return " ORDER BY (" + selectPerformerPlayCountSQL + ") " + direction +} + +func (qb *PerformerStore) sortByLastOAt(direction string) string { + // need to get the o_dates from scenes + return " ORDER BY (" + selectPerformerLastOAtSQL + ") " + direction +} + +func (qb *PerformerStore) sortByLastPlayedAt(direction string) string { + // need to get the view_dates from scenes + return " ORDER BY (" + selectPerformerLastPlayedAtSQL + ") " + direction +} + func (qb *PerformerStore) getPerformerSort(findFilter *models.FindFilterType) string { var sort string var direction string @@ -1048,8 +1134,14 @@ func (qb *PerformerStore) getPerformerSort(findFilter *models.FindFilterType) st sortQuery += getCountSort(performerTable, performersImagesTable, performerIDColumn, direction) case "galleries_count": sortQuery += getCountSort(performerTable, performersGalleriesTable, performerIDColumn, direction) + case "play_count": + sortQuery += qb.sortByPlayCount(direction) case "o_counter": sortQuery += qb.sortByOCounter(direction) + case "last_played_at": + sortQuery += qb.sortByLastPlayedAt(direction) + case "last_o_at": + sortQuery += qb.sortByLastOAt(direction) default: sortQuery += getSort(sort, direction, "performers") } diff --git a/ui/v2.5/src/components/Performers/PerformerListTable.tsx b/ui/v2.5/src/components/Performers/PerformerListTable.tsx index 183cfbf3a..756efa979 100644 --- a/ui/v2.5/src/components/Performers/PerformerListTable.tsx +++ b/ui/v2.5/src/components/Performers/PerformerListTable.tsx @@ -350,7 +350,7 @@ export const PerformerListTable: React.FC = ( }, { value: "o_counter", - label: intl.formatMessage({ id: "o_counter" }), + label: intl.formatMessage({ id: "o_count" }), defaultShow: true, render: OCounterCell, }, diff --git a/ui/v2.5/src/components/Scenes/SceneListTable.tsx b/ui/v2.5/src/components/Scenes/SceneListTable.tsx index 2e2c969fb..5500d096e 100644 --- a/ui/v2.5/src/components/Scenes/SceneListTable.tsx +++ b/ui/v2.5/src/components/Scenes/SceneListTable.tsx @@ -334,7 +334,7 @@ export const SceneListTable: React.FC = ( }, { value: "o_counter", - label: intl.formatMessage({ id: "o_counter" }), + label: intl.formatMessage({ id: "o_count" }), render: (s) => <>{s.o_counter}, }, { diff --git a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx index 676c2303b..55301f926 100644 --- a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx @@ -410,7 +410,7 @@ const SceneMergeDetails: React.FC = ({ onChange={(value) => setRating(value)} /> (