mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add penis length and circumcision stats to performers. (#3627)
* Add penis length stat to performers. * Modified the UI to display and edit the stat. * Added the ability to filter floats to allow filtering by penis length. * Add circumcision stat to performer. * Refactor enum filtering * Change boolean filter to radio buttons * Return null for empty enum values --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -52,6 +52,8 @@ func Test_PerformerStore_Create(t *testing.T) {
|
||||
height = 134
|
||||
measurements = "measurements"
|
||||
fakeTits = "fakeTits"
|
||||
penisLength = 1.23
|
||||
circumcised = models.CircumisedEnumCut
|
||||
careerLength = "careerLength"
|
||||
tattoos = "tattoos"
|
||||
piercings = "piercings"
|
||||
@@ -81,7 +83,7 @@ func Test_PerformerStore_Create(t *testing.T) {
|
||||
models.Performer{
|
||||
Name: name,
|
||||
Disambiguation: disambiguation,
|
||||
Gender: gender,
|
||||
Gender: &gender,
|
||||
URL: url,
|
||||
Twitter: twitter,
|
||||
Instagram: instagram,
|
||||
@@ -92,6 +94,8 @@ func Test_PerformerStore_Create(t *testing.T) {
|
||||
Height: &height,
|
||||
Measurements: measurements,
|
||||
FakeTits: fakeTits,
|
||||
PenisLength: &penisLength,
|
||||
Circumcised: &circumcised,
|
||||
CareerLength: careerLength,
|
||||
Tattoos: tattoos,
|
||||
Piercings: piercings,
|
||||
@@ -196,6 +200,8 @@ func Test_PerformerStore_Update(t *testing.T) {
|
||||
height = 134
|
||||
measurements = "measurements"
|
||||
fakeTits = "fakeTits"
|
||||
penisLength = 1.23
|
||||
circumcised = models.CircumisedEnumCut
|
||||
careerLength = "careerLength"
|
||||
tattoos = "tattoos"
|
||||
piercings = "piercings"
|
||||
@@ -226,7 +232,7 @@ func Test_PerformerStore_Update(t *testing.T) {
|
||||
ID: performerIDs[performerIdxWithGallery],
|
||||
Name: name,
|
||||
Disambiguation: disambiguation,
|
||||
Gender: gender,
|
||||
Gender: &gender,
|
||||
URL: url,
|
||||
Twitter: twitter,
|
||||
Instagram: instagram,
|
||||
@@ -237,6 +243,8 @@ func Test_PerformerStore_Update(t *testing.T) {
|
||||
Height: &height,
|
||||
Measurements: measurements,
|
||||
FakeTits: fakeTits,
|
||||
PenisLength: &penisLength,
|
||||
Circumcised: &circumcised,
|
||||
CareerLength: careerLength,
|
||||
Tattoos: tattoos,
|
||||
Piercings: piercings,
|
||||
@@ -327,6 +335,7 @@ func clearPerformerPartial() models.PerformerPartial {
|
||||
nullString := models.OptionalString{Set: true, Null: true}
|
||||
nullDate := models.OptionalDate{Set: true, Null: true}
|
||||
nullInt := models.OptionalInt{Set: true, Null: true}
|
||||
nullFloat := models.OptionalFloat64{Set: true, Null: true}
|
||||
|
||||
// leave mandatory fields
|
||||
return models.PerformerPartial{
|
||||
@@ -342,6 +351,8 @@ func clearPerformerPartial() models.PerformerPartial {
|
||||
Height: nullInt,
|
||||
Measurements: nullString,
|
||||
FakeTits: nullString,
|
||||
PenisLength: nullFloat,
|
||||
Circumcised: nullString,
|
||||
CareerLength: nullString,
|
||||
Tattoos: nullString,
|
||||
Piercings: nullString,
|
||||
@@ -372,6 +383,8 @@ func Test_PerformerStore_UpdatePartial(t *testing.T) {
|
||||
height = 143
|
||||
measurements = "measurements"
|
||||
fakeTits = "fakeTits"
|
||||
penisLength = 1.23
|
||||
circumcised = models.CircumisedEnumCut
|
||||
careerLength = "careerLength"
|
||||
tattoos = "tattoos"
|
||||
piercings = "piercings"
|
||||
@@ -415,6 +428,8 @@ func Test_PerformerStore_UpdatePartial(t *testing.T) {
|
||||
Height: models.NewOptionalInt(height),
|
||||
Measurements: models.NewOptionalString(measurements),
|
||||
FakeTits: models.NewOptionalString(fakeTits),
|
||||
PenisLength: models.NewOptionalFloat64(penisLength),
|
||||
Circumcised: models.NewOptionalString(circumcised.String()),
|
||||
CareerLength: models.NewOptionalString(careerLength),
|
||||
Tattoos: models.NewOptionalString(tattoos),
|
||||
Piercings: models.NewOptionalString(piercings),
|
||||
@@ -453,7 +468,7 @@ func Test_PerformerStore_UpdatePartial(t *testing.T) {
|
||||
ID: performerIDs[performerIdxWithDupName],
|
||||
Name: name,
|
||||
Disambiguation: disambiguation,
|
||||
Gender: gender,
|
||||
Gender: &gender,
|
||||
URL: url,
|
||||
Twitter: twitter,
|
||||
Instagram: instagram,
|
||||
@@ -464,6 +479,8 @@ func Test_PerformerStore_UpdatePartial(t *testing.T) {
|
||||
Height: &height,
|
||||
Measurements: measurements,
|
||||
FakeTits: fakeTits,
|
||||
PenisLength: &penisLength,
|
||||
Circumcised: &circumcised,
|
||||
CareerLength: careerLength,
|
||||
Tattoos: tattoos,
|
||||
Piercings: piercings,
|
||||
@@ -957,16 +974,30 @@ func TestPerformerQuery(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"alias",
|
||||
"circumcised (cut)",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
Aliases: &models.StringCriterionInput{
|
||||
Value: getPerformerStringValue(performerIdxWithGallery, "alias"),
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
Circumcised: &models.CircumcisionCriterionInput{
|
||||
Value: []models.CircumisedEnum{models.CircumisedEnumCut},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
},
|
||||
},
|
||||
[]int{performerIdxWithGallery},
|
||||
[]int{performerIdxWithScene},
|
||||
[]int{performerIdx1WithScene},
|
||||
[]int{performerIdxWithScene, performerIdx2WithScene},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"circumcised (excludes cut)",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
Circumcised: &models.CircumcisionCriterionInput{
|
||||
Value: []models.CircumisedEnum{models.CircumisedEnumCut},
|
||||
Modifier: models.CriterionModifierExcludes,
|
||||
},
|
||||
},
|
||||
[]int{performerIdx2WithScene},
|
||||
// performerIdxWithScene has null value
|
||||
[]int{performerIdx1WithScene, performerIdxWithScene},
|
||||
false,
|
||||
},
|
||||
}
|
||||
@@ -995,6 +1026,107 @@ func TestPerformerQuery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerformerQueryPenisLength(t *testing.T) {
|
||||
var upper = 4.0
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
modifier models.CriterionModifier
|
||||
value float64
|
||||
value2 *float64
|
||||
}{
|
||||
{
|
||||
"equals",
|
||||
models.CriterionModifierEquals,
|
||||
1,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"not equals",
|
||||
models.CriterionModifierNotEquals,
|
||||
1,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"greater than",
|
||||
models.CriterionModifierGreaterThan,
|
||||
1,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"between",
|
||||
models.CriterionModifierBetween,
|
||||
2,
|
||||
&upper,
|
||||
},
|
||||
{
|
||||
"greater than",
|
||||
models.CriterionModifierNotBetween,
|
||||
2,
|
||||
&upper,
|
||||
},
|
||||
{
|
||||
"null",
|
||||
models.CriterionModifierIsNull,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"not null",
|
||||
models.CriterionModifierNotNull,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
|
||||
filter := &models.PerformerFilterType{
|
||||
PenisLength: &models.FloatCriterionInput{
|
||||
Modifier: tt.modifier,
|
||||
Value: tt.value,
|
||||
Value2: tt.value2,
|
||||
},
|
||||
}
|
||||
|
||||
performers, _, err := db.Performer.Query(ctx, filter, nil)
|
||||
if err != nil {
|
||||
t.Errorf("PerformerStore.Query() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, p := range performers {
|
||||
verifyFloat(t, p.PenisLength, *filter.PenisLength)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func verifyFloat(t *testing.T, value *float64, criterion models.FloatCriterionInput) bool {
|
||||
t.Helper()
|
||||
assert := assert.New(t)
|
||||
switch criterion.Modifier {
|
||||
case models.CriterionModifierEquals:
|
||||
return assert.NotNil(value) && assert.Equal(criterion.Value, *value)
|
||||
case models.CriterionModifierNotEquals:
|
||||
return assert.NotNil(value) && assert.NotEqual(criterion.Value, *value)
|
||||
case models.CriterionModifierGreaterThan:
|
||||
return assert.NotNil(value) && assert.Greater(*value, criterion.Value)
|
||||
case models.CriterionModifierLessThan:
|
||||
return assert.NotNil(value) && assert.Less(*value, criterion.Value)
|
||||
case models.CriterionModifierBetween:
|
||||
return assert.NotNil(value) && assert.GreaterOrEqual(*value, criterion.Value) && assert.LessOrEqual(*value, *criterion.Value2)
|
||||
case models.CriterionModifierNotBetween:
|
||||
return assert.NotNil(value) && assert.True(*value < criterion.Value || *value > *criterion.Value2)
|
||||
case models.CriterionModifierIsNull:
|
||||
return assert.Nil(value)
|
||||
case models.CriterionModifierNotNull:
|
||||
return assert.NotNil(value)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func TestPerformerQueryForAutoTag(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
tqb := db.Performer
|
||||
|
||||
Reference in New Issue
Block a user