mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user