Containing Group/Sub-Group relationships (#5105)

* Add UI support for setting containing groups
* Show containing groups in group details panel
* Move tag hierarchical filter code into separate type
* Add depth to scene_count and add sub_group_count
* Add sub-groups tab to groups page
* Add containing groups to edit groups dialog
* Show containing group description in sub-group view
* Show group scene number in group scenes view
* Add ability to drag move grid cards
* Add sub group order option
* Add reorder sub-groups interface
* Separate page size selector component
* Add interfaces to add and remove sub-groups to a group
* Separate MultiSet components
* Allow setting description while setting containing groups
This commit is contained in:
WithoutPants
2024-08-30 11:43:44 +10:00
committed by GitHub
parent 96fdd94a01
commit bcf0fda7ac
99 changed files with 5388 additions and 935 deletions

View File

@@ -16,6 +16,7 @@ import (
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/sliceutil"
"github.com/stashapp/stash/pkg/sliceutil/intslice"
"github.com/stretchr/testify/assert"
)
@@ -2217,7 +2218,7 @@ func TestSceneQuery(t *testing.T) {
},
})
if (err != nil) != tt.wantErr {
t.Errorf("PerformerStore.Query() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("SceneStore.Query() error = %v, wantErr %v", err, tt.wantErr)
return
}
@@ -3873,6 +3874,100 @@ func TestSceneQueryStudioDepth(t *testing.T) {
})
}
func TestSceneGroups(t *testing.T) {
type criterion struct {
valueIdxs []int
modifier models.CriterionModifier
depth int
}
tests := []struct {
name string
c criterion
q string
includeIdxs []int
excludeIdxs []int
}{
{
"includes",
criterion{
[]int{groupIdxWithScene},
models.CriterionModifierIncludes,
0,
},
"",
[]int{sceneIdxWithGroup},
nil,
},
{
"excludes",
criterion{
[]int{groupIdxWithScene},
models.CriterionModifierExcludes,
0,
},
getSceneStringValue(sceneIdxWithGroup, titleField),
nil,
[]int{sceneIdxWithGroup},
},
{
"includes (depth = 1)",
criterion{
[]int{groupIdxWithChildWithScene},
models.CriterionModifierIncludes,
1,
},
"",
[]int{sceneIdxWithGroupWithParent},
nil,
},
}
for _, tt := range tests {
valueIDs := indexesToIDs(groupIDs, tt.c.valueIdxs)
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
assert := assert.New(t)
sceneFilter := &models.SceneFilterType{
Groups: &models.HierarchicalMultiCriterionInput{
Value: intslice.IntSliceToStringSlice(valueIDs),
Modifier: tt.c.modifier,
},
}
if tt.c.depth != 0 {
sceneFilter.Groups.Depth = &tt.c.depth
}
findFilter := &models.FindFilterType{}
if tt.q != "" {
findFilter.Q = &tt.q
}
results, err := db.Scene.Query(ctx, models.SceneQueryOptions{
SceneFilter: sceneFilter,
QueryOptions: models.QueryOptions{
FindFilter: findFilter,
},
})
if err != nil {
t.Errorf("SceneStore.Query() error = %v", err)
return
}
include := indexesToIDs(sceneIDs, tt.includeIdxs)
exclude := indexesToIDs(sceneIDs, tt.excludeIdxs)
assert.Subset(results.IDs, include)
for _, e := range exclude {
assert.NotContains(results.IDs, e)
}
})
}
}
func TestSceneQueryMovies(t *testing.T) {
withTxn(func(ctx context.Context) error {
sqb := db.Scene
@@ -4188,78 +4283,6 @@ func verifyScenesPerformerCount(t *testing.T, performerCountCriterion models.Int
})
}
func TestSceneCountByTagID(t *testing.T) {
withTxn(func(ctx context.Context) error {
sqb := db.Scene
sceneCount, err := sqb.CountByTagID(ctx, tagIDs[tagIdxWithScene])
if err != nil {
t.Errorf("error calling CountByTagID: %s", err.Error())
}
assert.Equal(t, 1, sceneCount)
sceneCount, err = sqb.CountByTagID(ctx, 0)
if err != nil {
t.Errorf("error calling CountByTagID: %s", err.Error())
}
assert.Equal(t, 0, sceneCount)
return nil
})
}
func TestSceneCountByGroupID(t *testing.T) {
withTxn(func(ctx context.Context) error {
sqb := db.Scene
sceneCount, err := sqb.CountByGroupID(ctx, groupIDs[groupIdxWithScene])
if err != nil {
t.Errorf("error calling CountByGroupID: %s", err.Error())
}
assert.Equal(t, 1, sceneCount)
sceneCount, err = sqb.CountByGroupID(ctx, 0)
if err != nil {
t.Errorf("error calling CountByGroupID: %s", err.Error())
}
assert.Equal(t, 0, sceneCount)
return nil
})
}
func TestSceneCountByStudioID(t *testing.T) {
withTxn(func(ctx context.Context) error {
sqb := db.Scene
sceneCount, err := sqb.CountByStudioID(ctx, studioIDs[studioIdxWithScene])
if err != nil {
t.Errorf("error calling CountByStudioID: %s", err.Error())
}
assert.Equal(t, 1, sceneCount)
sceneCount, err = sqb.CountByStudioID(ctx, 0)
if err != nil {
t.Errorf("error calling CountByStudioID: %s", err.Error())
}
assert.Equal(t, 0, sceneCount)
return nil
})
}
func TestFindByMovieID(t *testing.T) {
withTxn(func(ctx context.Context) error {
sqb := db.Scene