mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Add date filters (#2834)
* graphql: support date and timestamp filter types * sql: add support for date & timestamp criterions * ui: add support for date and timestamp criterions * scenes: add support for filtering by date, created at and updated at * image: support filtering by created at and updated at * gallery: support filtering by date, created at and updated at * movie: support filtering by date, created at and updated at * studio: support filtering by date, created at and updated at * tag: support filtering by date, created at and updated at * performer: support filtering by bitrh & death date and created & updated at * marker: support filtering by created & updated at and scene date, created & updated at
This commit is contained in:
@@ -105,6 +105,14 @@ input PerformerFilterType {
|
|||||||
studios: HierarchicalMultiCriterionInput
|
studios: HierarchicalMultiCriterionInput
|
||||||
"""Filter by autotag ignore value"""
|
"""Filter by autotag ignore value"""
|
||||||
ignore_auto_tag: Boolean
|
ignore_auto_tag: Boolean
|
||||||
|
"""Filter by birthdate"""
|
||||||
|
birthdate: DateCriterionInput
|
||||||
|
"""Filter by death date"""
|
||||||
|
death_date: DateCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input SceneMarkerFilterType {
|
input SceneMarkerFilterType {
|
||||||
@@ -116,6 +124,16 @@ input SceneMarkerFilterType {
|
|||||||
scene_tags: HierarchicalMultiCriterionInput
|
scene_tags: HierarchicalMultiCriterionInput
|
||||||
"""Filter to only include scene markers with these performers"""
|
"""Filter to only include scene markers with these performers"""
|
||||||
performers: MultiCriterionInput
|
performers: MultiCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
|
"""Filter by scene date"""
|
||||||
|
scene_date: DateCriterionInput
|
||||||
|
"""Filter by cscene reation time"""
|
||||||
|
scene_created_at: TimestampCriterionInput
|
||||||
|
"""Filter by lscene ast update time"""
|
||||||
|
scene_updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input SceneFilterType {
|
input SceneFilterType {
|
||||||
@@ -183,6 +201,12 @@ input SceneFilterType {
|
|||||||
interactive_speed: IntCriterionInput
|
interactive_speed: IntCriterionInput
|
||||||
"""Filter by captions"""
|
"""Filter by captions"""
|
||||||
captions: StringCriterionInput
|
captions: StringCriterionInput
|
||||||
|
"""Filter by date"""
|
||||||
|
date: DateCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input MovieFilterType {
|
input MovieFilterType {
|
||||||
@@ -203,6 +227,12 @@ input MovieFilterType {
|
|||||||
url: StringCriterionInput
|
url: StringCriterionInput
|
||||||
"""Filter to only include movies where performer appears in a scene"""
|
"""Filter to only include movies where performer appears in a scene"""
|
||||||
performers: MultiCriterionInput
|
performers: MultiCriterionInput
|
||||||
|
"""Filter by date"""
|
||||||
|
date: DateCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input StudioFilterType {
|
input StudioFilterType {
|
||||||
@@ -232,6 +262,10 @@ input StudioFilterType {
|
|||||||
aliases: StringCriterionInput
|
aliases: StringCriterionInput
|
||||||
"""Filter by autotag ignore value"""
|
"""Filter by autotag ignore value"""
|
||||||
ignore_auto_tag: Boolean
|
ignore_auto_tag: Boolean
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input GalleryFilterType {
|
input GalleryFilterType {
|
||||||
@@ -279,6 +313,12 @@ input GalleryFilterType {
|
|||||||
image_count: IntCriterionInput
|
image_count: IntCriterionInput
|
||||||
"""Filter by url"""
|
"""Filter by url"""
|
||||||
url: StringCriterionInput
|
url: StringCriterionInput
|
||||||
|
"""Filter by date"""
|
||||||
|
date: DateCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input TagFilterType {
|
input TagFilterType {
|
||||||
@@ -327,6 +367,12 @@ input TagFilterType {
|
|||||||
|
|
||||||
"""Filter by autotag ignore value"""
|
"""Filter by autotag ignore value"""
|
||||||
ignore_auto_tag: Boolean
|
ignore_auto_tag: Boolean
|
||||||
|
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input ImageFilterType {
|
input ImageFilterType {
|
||||||
@@ -370,6 +416,10 @@ input ImageFilterType {
|
|||||||
performer_favorite: Boolean
|
performer_favorite: Boolean
|
||||||
"""Filter to only include images with these galleries"""
|
"""Filter to only include images with these galleries"""
|
||||||
galleries: MultiCriterionInput
|
galleries: MultiCriterionInput
|
||||||
|
"""Filter by creation time"""
|
||||||
|
created_at: TimestampCriterionInput
|
||||||
|
"""Filter by last update time"""
|
||||||
|
updated_at: TimestampCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CriterionModifier {
|
enum CriterionModifier {
|
||||||
@@ -426,6 +476,18 @@ input HierarchicalMultiCriterionInput {
|
|||||||
depth: Int
|
depth: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input DateCriterionInput {
|
||||||
|
value: String!
|
||||||
|
value2: String
|
||||||
|
modifier: CriterionModifier!
|
||||||
|
}
|
||||||
|
|
||||||
|
input TimestampCriterionInput {
|
||||||
|
value: String!
|
||||||
|
value2: String
|
||||||
|
modifier: CriterionModifier!
|
||||||
|
}
|
||||||
|
|
||||||
enum FilterMode {
|
enum FilterMode {
|
||||||
SCENES,
|
SCENES,
|
||||||
PERFORMERS,
|
PERFORMERS,
|
||||||
|
|||||||
@@ -124,3 +124,15 @@ type MultiCriterionInput struct {
|
|||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
Modifier CriterionModifier `json:"modifier"`
|
Modifier CriterionModifier `json:"modifier"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DateCriterionInput struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
Value2 *string `json:"value2"`
|
||||||
|
Modifier CriterionModifier `json:"modifier"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimestampCriterionInput struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
Value2 *string `json:"value2"`
|
||||||
|
Modifier CriterionModifier `json:"modifier"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ type GalleryFilterType struct {
|
|||||||
ImageCount *IntCriterionInput `json:"image_count"`
|
ImageCount *IntCriterionInput `json:"image_count"`
|
||||||
// Filter by url
|
// Filter by url
|
||||||
URL *StringCriterionInput `json:"url"`
|
URL *StringCriterionInput `json:"url"`
|
||||||
|
// Filter by date
|
||||||
|
Date *DateCriterionInput `json:"date"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GalleryUpdateInput struct {
|
type GalleryUpdateInput struct {
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ type ImageFilterType struct {
|
|||||||
PerformerFavorite *bool `json:"performer_favorite"`
|
PerformerFavorite *bool `json:"performer_favorite"`
|
||||||
// Filter to only include images with these galleries
|
// Filter to only include images with these galleries
|
||||||
Galleries *MultiCriterionInput `json:"galleries"`
|
Galleries *MultiCriterionInput `json:"galleries"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageDestroyInput struct {
|
type ImageDestroyInput struct {
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ type MovieFilterType struct {
|
|||||||
URL *StringCriterionInput `json:"url"`
|
URL *StringCriterionInput `json:"url"`
|
||||||
// Filter to only include movies where performer appears in a scene
|
// Filter to only include movies where performer appears in a scene
|
||||||
Performers *MultiCriterionInput `json:"performers"`
|
Performers *MultiCriterionInput `json:"performers"`
|
||||||
|
// Filter by date
|
||||||
|
Date *DateCriterionInput `json:"date"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MovieReader interface {
|
type MovieReader interface {
|
||||||
|
|||||||
@@ -125,6 +125,14 @@ type PerformerFilterType struct {
|
|||||||
Studios *HierarchicalMultiCriterionInput `json:"studios"`
|
Studios *HierarchicalMultiCriterionInput `json:"studios"`
|
||||||
// Filter by autotag ignore value
|
// Filter by autotag ignore value
|
||||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||||
|
// Filter by birthdate
|
||||||
|
Birthdate *DateCriterionInput `json:"birth_date"`
|
||||||
|
// Filter by death date
|
||||||
|
DeathDate *DateCriterionInput `json:"death_date"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PerformerFinder interface {
|
type PerformerFinder interface {
|
||||||
|
|||||||
@@ -75,6 +75,12 @@ type SceneFilterType struct {
|
|||||||
InteractiveSpeed *IntCriterionInput `json:"interactive_speed"`
|
InteractiveSpeed *IntCriterionInput `json:"interactive_speed"`
|
||||||
|
|
||||||
Captions *StringCriterionInput `json:"captions"`
|
Captions *StringCriterionInput `json:"captions"`
|
||||||
|
// Filter by date
|
||||||
|
Date *DateCriterionInput `json:"date"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SceneQueryOptions struct {
|
type SceneQueryOptions struct {
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ type SceneMarkerFilterType struct {
|
|||||||
SceneTags *HierarchicalMultiCriterionInput `json:"scene_tags"`
|
SceneTags *HierarchicalMultiCriterionInput `json:"scene_tags"`
|
||||||
// Filter to only include scene markers with these performers
|
// Filter to only include scene markers with these performers
|
||||||
Performers *MultiCriterionInput `json:"performers"`
|
Performers *MultiCriterionInput `json:"performers"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
|
// Filter by scenes date
|
||||||
|
SceneDate *DateCriterionInput `json:"scene_date"`
|
||||||
|
// Filter by scenes created at
|
||||||
|
SceneCreatedAt *TimestampCriterionInput `json:"scene_created_at"`
|
||||||
|
// Filter by scenes updated at
|
||||||
|
SceneUpdatedAt *TimestampCriterionInput `json:"scene_updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MarkerStringsResultType struct {
|
type MarkerStringsResultType struct {
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ type StudioFilterType struct {
|
|||||||
Aliases *StringCriterionInput `json:"aliases"`
|
Aliases *StringCriterionInput `json:"aliases"`
|
||||||
// Filter by autotag ignore value
|
// Filter by autotag ignore value
|
||||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StudioFinder interface {
|
type StudioFinder interface {
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ type TagFilterType struct {
|
|||||||
ChildCount *IntCriterionInput `json:"child_count"`
|
ChildCount *IntCriterionInput `json:"child_count"`
|
||||||
// Filter by autotag ignore value
|
// Filter by autotag ignore value
|
||||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||||
|
// Filter by created at
|
||||||
|
CreatedAt *TimestampCriterionInput `json:"created_at"`
|
||||||
|
// Filter by updated at
|
||||||
|
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TagFinder interface {
|
type TagFinder interface {
|
||||||
|
|||||||
@@ -543,6 +543,24 @@ func boolCriterionHandler(c *bool, column string, addJoinFn func(f *filterBuilde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dateCriterionHandler(c *models.DateCriterionInput, column string) criterionHandlerFunc {
|
||||||
|
return func(ctx context.Context, f *filterBuilder) {
|
||||||
|
if c != nil {
|
||||||
|
clause, args := getDateCriterionWhereClause(column, *c)
|
||||||
|
f.addWhere(clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func timestampCriterionHandler(c *models.TimestampCriterionInput, column string) criterionHandlerFunc {
|
||||||
|
return func(ctx context.Context, f *filterBuilder) {
|
||||||
|
if c != nil {
|
||||||
|
clause, args := getTimestampCriterionWhereClause(column, *c)
|
||||||
|
f.addWhere(clause, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handle for MultiCriterion where there is a join table between the new
|
// handle for MultiCriterion where there is a join table between the new
|
||||||
// objects
|
// objects
|
||||||
type joinedMultiCriterionHandlerBuilder struct {
|
type joinedMultiCriterionHandlerBuilder struct {
|
||||||
|
|||||||
@@ -665,6 +665,9 @@ func (qb *GalleryStore) makeFilter(ctx context.Context, galleryFilter *models.Ga
|
|||||||
query.handleCriterion(ctx, galleryImageCountCriterionHandler(qb, galleryFilter.ImageCount))
|
query.handleCriterion(ctx, galleryImageCountCriterionHandler(qb, galleryFilter.ImageCount))
|
||||||
query.handleCriterion(ctx, galleryPerformerFavoriteCriterionHandler(galleryFilter.PerformerFavorite))
|
query.handleCriterion(ctx, galleryPerformerFavoriteCriterionHandler(galleryFilter.PerformerFavorite))
|
||||||
query.handleCriterion(ctx, galleryPerformerAgeCriterionHandler(galleryFilter.PerformerAge))
|
query.handleCriterion(ctx, galleryPerformerAgeCriterionHandler(galleryFilter.PerformerAge))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(galleryFilter.Date, "galleries.date"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(galleryFilter.CreatedAt, "galleries.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(galleryFilter.UpdatedAt, "galleries.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -647,6 +647,8 @@ func (qb *ImageStore) makeFilter(ctx context.Context, imageFilter *models.ImageF
|
|||||||
query.handleCriterion(ctx, imageStudioCriterionHandler(qb, imageFilter.Studios))
|
query.handleCriterion(ctx, imageStudioCriterionHandler(qb, imageFilter.Studios))
|
||||||
query.handleCriterion(ctx, imagePerformerTagsCriterionHandler(qb, imageFilter.PerformerTags))
|
query.handleCriterion(ctx, imagePerformerTagsCriterionHandler(qb, imageFilter.PerformerTags))
|
||||||
query.handleCriterion(ctx, imagePerformerFavoriteCriterionHandler(imageFilter.PerformerFavorite))
|
query.handleCriterion(ctx, imagePerformerFavoriteCriterionHandler(imageFilter.PerformerFavorite))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(imageFilter.CreatedAt, "images.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(imageFilter.UpdatedAt, "images.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ func (qb *movieQueryBuilder) makeFilter(ctx context.Context, movieFilter *models
|
|||||||
query.handleCriterion(ctx, stringCriterionHandler(movieFilter.URL, "movies.url"))
|
query.handleCriterion(ctx, stringCriterionHandler(movieFilter.URL, "movies.url"))
|
||||||
query.handleCriterion(ctx, movieStudioCriterionHandler(qb, movieFilter.Studios))
|
query.handleCriterion(ctx, movieStudioCriterionHandler(qb, movieFilter.Studios))
|
||||||
query.handleCriterion(ctx, moviePerformersCriterionHandler(qb, movieFilter.Performers))
|
query.handleCriterion(ctx, moviePerformersCriterionHandler(qb, movieFilter.Performers))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(movieFilter.Date, "movies.date"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(movieFilter.CreatedAt, "movies.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(movieFilter.UpdatedAt, "movies.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -541,6 +541,10 @@ func (qb *PerformerStore) makeFilter(ctx context.Context, filter *models.Perform
|
|||||||
query.handleCriterion(ctx, performerSceneCountCriterionHandler(qb, filter.SceneCount))
|
query.handleCriterion(ctx, performerSceneCountCriterionHandler(qb, filter.SceneCount))
|
||||||
query.handleCriterion(ctx, performerImageCountCriterionHandler(qb, filter.ImageCount))
|
query.handleCriterion(ctx, performerImageCountCriterionHandler(qb, filter.ImageCount))
|
||||||
query.handleCriterion(ctx, performerGalleryCountCriterionHandler(qb, filter.GalleryCount))
|
query.handleCriterion(ctx, performerGalleryCountCriterionHandler(qb, filter.GalleryCount))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(filter.Birthdate, tableName+".birthdate"))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(filter.DeathDate, tableName+".death_date"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(filter.CreatedAt, tableName+".created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(filter.UpdatedAt, tableName+".updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -877,6 +877,9 @@ func (qb *SceneStore) makeFilter(ctx context.Context, sceneFilter *models.SceneF
|
|||||||
query.handleCriterion(ctx, scenePerformerFavoriteCriterionHandler(sceneFilter.PerformerFavorite))
|
query.handleCriterion(ctx, scenePerformerFavoriteCriterionHandler(sceneFilter.PerformerFavorite))
|
||||||
query.handleCriterion(ctx, scenePerformerAgeCriterionHandler(sceneFilter.PerformerAge))
|
query.handleCriterion(ctx, scenePerformerAgeCriterionHandler(sceneFilter.PerformerAge))
|
||||||
query.handleCriterion(ctx, scenePhashDuplicatedCriterionHandler(sceneFilter.Duplicated, qb.addSceneFilesTable))
|
query.handleCriterion(ctx, scenePhashDuplicatedCriterionHandler(sceneFilter.Duplicated, qb.addSceneFilesTable))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(sceneFilter.Date, "scenes.date"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneFilter.CreatedAt, "scenes.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneFilter.UpdatedAt, "scenes.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,11 @@ func (qb *sceneMarkerQueryBuilder) makeFilter(ctx context.Context, sceneMarkerFi
|
|||||||
query.handleCriterion(ctx, sceneMarkerTagsCriterionHandler(qb, sceneMarkerFilter.Tags))
|
query.handleCriterion(ctx, sceneMarkerTagsCriterionHandler(qb, sceneMarkerFilter.Tags))
|
||||||
query.handleCriterion(ctx, sceneMarkerSceneTagsCriterionHandler(qb, sceneMarkerFilter.SceneTags))
|
query.handleCriterion(ctx, sceneMarkerSceneTagsCriterionHandler(qb, sceneMarkerFilter.SceneTags))
|
||||||
query.handleCriterion(ctx, sceneMarkerPerformersCriterionHandler(qb, sceneMarkerFilter.Performers))
|
query.handleCriterion(ctx, sceneMarkerPerformersCriterionHandler(qb, sceneMarkerFilter.Performers))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneMarkerFilter.CreatedAt, "scene_markers.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneMarkerFilter.UpdatedAt, "scene_markers.updated_at"))
|
||||||
|
query.handleCriterion(ctx, dateCriterionHandler(sceneMarkerFilter.SceneDate, "scenes.date"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneMarkerFilter.SceneCreatedAt, "scenes.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(sceneMarkerFilter.SceneUpdatedAt, "scenes.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
@@ -181,6 +182,76 @@ func getIntWhereClause(column string, modifier models.CriterionModifier, value i
|
|||||||
panic("unsupported int modifier type " + modifier)
|
panic("unsupported int modifier type " + modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDateCriterionWhereClause(column string, input models.DateCriterionInput) (string, []interface{}) {
|
||||||
|
return getDateWhereClause(column, input.Modifier, input.Value, input.Value2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDateWhereClause(column string, modifier models.CriterionModifier, value string, upper *string) (string, []interface{}) {
|
||||||
|
if upper == nil {
|
||||||
|
u := time.Now().AddDate(0, 0, 1).Format(time.RFC3339)
|
||||||
|
upper = &u
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []interface{}{value}
|
||||||
|
betweenArgs := []interface{}{value, *upper}
|
||||||
|
|
||||||
|
switch modifier {
|
||||||
|
case models.CriterionModifierIsNull:
|
||||||
|
return fmt.Sprintf("(%s IS NULL OR %s = '')", column, column), nil
|
||||||
|
case models.CriterionModifierNotNull:
|
||||||
|
return fmt.Sprintf("(%s IS NOT NULL AND %s != '')", column, column), nil
|
||||||
|
case models.CriterionModifierEquals:
|
||||||
|
return fmt.Sprintf("%s = ?", column), args
|
||||||
|
case models.CriterionModifierNotEquals:
|
||||||
|
return fmt.Sprintf("%s != ?", column), args
|
||||||
|
case models.CriterionModifierBetween:
|
||||||
|
return fmt.Sprintf("%s BETWEEN ? AND ?", column), betweenArgs
|
||||||
|
case models.CriterionModifierNotBetween:
|
||||||
|
return fmt.Sprintf("%s NOT BETWEEN ? AND ?", column), betweenArgs
|
||||||
|
case models.CriterionModifierLessThan:
|
||||||
|
return fmt.Sprintf("%s < ?", column), args
|
||||||
|
case models.CriterionModifierGreaterThan:
|
||||||
|
return fmt.Sprintf("%s > ?", column), args
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unsupported date modifier type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimestampCriterionWhereClause(column string, input models.TimestampCriterionInput) (string, []interface{}) {
|
||||||
|
return getTimestampWhereClause(column, input.Modifier, input.Value, input.Value2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimestampWhereClause(column string, modifier models.CriterionModifier, value string, upper *string) (string, []interface{}) {
|
||||||
|
if upper == nil {
|
||||||
|
u := time.Now().AddDate(0, 0, 1).Format(time.RFC3339)
|
||||||
|
upper = &u
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []interface{}{value}
|
||||||
|
betweenArgs := []interface{}{value, *upper}
|
||||||
|
|
||||||
|
switch modifier {
|
||||||
|
case models.CriterionModifierIsNull:
|
||||||
|
return fmt.Sprintf("%s IS NULL", column), nil
|
||||||
|
case models.CriterionModifierNotNull:
|
||||||
|
return fmt.Sprintf("%s IS NOT NULL", column), nil
|
||||||
|
case models.CriterionModifierEquals:
|
||||||
|
return fmt.Sprintf("%s = ?", column), args
|
||||||
|
case models.CriterionModifierNotEquals:
|
||||||
|
return fmt.Sprintf("%s != ?", column), args
|
||||||
|
case models.CriterionModifierBetween:
|
||||||
|
return fmt.Sprintf("%s BETWEEN ? AND ?", column), betweenArgs
|
||||||
|
case models.CriterionModifierNotBetween:
|
||||||
|
return fmt.Sprintf("%s NOT BETWEEN ? AND ?", column), betweenArgs
|
||||||
|
case models.CriterionModifierLessThan:
|
||||||
|
return fmt.Sprintf("%s < ?", column), args
|
||||||
|
case models.CriterionModifierGreaterThan:
|
||||||
|
return fmt.Sprintf("%s > ?", column), args
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unsupported date modifier type")
|
||||||
|
}
|
||||||
|
|
||||||
// returns where clause and having clause
|
// returns where clause and having clause
|
||||||
func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, foreignFK string, criterion *models.MultiCriterionInput) (string, string) {
|
func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, foreignFK string, criterion *models.MultiCriterionInput) (string, string) {
|
||||||
whereClause := ""
|
whereClause := ""
|
||||||
|
|||||||
@@ -250,6 +250,8 @@ func (qb *studioQueryBuilder) makeFilter(ctx context.Context, studioFilter *mode
|
|||||||
query.handleCriterion(ctx, studioGalleryCountCriterionHandler(qb, studioFilter.GalleryCount))
|
query.handleCriterion(ctx, studioGalleryCountCriterionHandler(qb, studioFilter.GalleryCount))
|
||||||
query.handleCriterion(ctx, studioParentCriterionHandler(qb, studioFilter.Parents))
|
query.handleCriterion(ctx, studioParentCriterionHandler(qb, studioFilter.Parents))
|
||||||
query.handleCriterion(ctx, studioAliasCriterionHandler(qb, studioFilter.Aliases))
|
query.handleCriterion(ctx, studioAliasCriterionHandler(qb, studioFilter.Aliases))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(studioFilter.CreatedAt, "studios.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(studioFilter.UpdatedAt, "studios.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,6 +338,8 @@ func (qb *tagQueryBuilder) makeFilter(ctx context.Context, tagFilter *models.Tag
|
|||||||
query.handleCriterion(ctx, tagChildrenCriterionHandler(qb, tagFilter.Children))
|
query.handleCriterion(ctx, tagChildrenCriterionHandler(qb, tagFilter.Children))
|
||||||
query.handleCriterion(ctx, tagParentCountCriterionHandler(qb, tagFilter.ParentCount))
|
query.handleCriterion(ctx, tagParentCountCriterionHandler(qb, tagFilter.ParentCount))
|
||||||
query.handleCriterion(ctx, tagChildCountCriterionHandler(qb, tagFilter.ChildCount))
|
query.handleCriterion(ctx, tagChildCountCriterionHandler(qb, tagFilter.ChildCount))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(tagFilter.CreatedAt, "tags.created_at"))
|
||||||
|
query.handleCriterion(ctx, timestampCriterionHandler(tagFilter.UpdatedAt, "tags.updated_at"))
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
IHierarchicalLabeledIdCriterion,
|
IHierarchicalLabeledIdCriterion,
|
||||||
NumberCriterion,
|
NumberCriterion,
|
||||||
ILabeledIdCriterion,
|
ILabeledIdCriterion,
|
||||||
|
DateCriterion,
|
||||||
|
TimestampCriterion,
|
||||||
} from "src/models/list-filter/criteria/criterion";
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import {
|
import {
|
||||||
NoneCriterion,
|
NoneCriterion,
|
||||||
@@ -20,6 +22,8 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||||||
import {
|
import {
|
||||||
criterionIsHierarchicalLabelValue,
|
criterionIsHierarchicalLabelValue,
|
||||||
criterionIsNumberValue,
|
criterionIsNumberValue,
|
||||||
|
criterionIsDateValue,
|
||||||
|
criterionIsTimestampValue,
|
||||||
CriterionType,
|
CriterionType,
|
||||||
} from "src/models/list-filter/types";
|
} from "src/models/list-filter/types";
|
||||||
import { DurationFilter } from "./Filters/DurationFilter";
|
import { DurationFilter } from "./Filters/DurationFilter";
|
||||||
@@ -28,6 +32,8 @@ import { LabeledIdFilter } from "./Filters/LabeledIdFilter";
|
|||||||
import { HierarchicalLabelValueFilter } from "./Filters/HierarchicalLabelValueFilter";
|
import { HierarchicalLabelValueFilter } from "./Filters/HierarchicalLabelValueFilter";
|
||||||
import { OptionsFilter } from "./Filters/OptionsFilter";
|
import { OptionsFilter } from "./Filters/OptionsFilter";
|
||||||
import { InputFilter } from "./Filters/InputFilter";
|
import { InputFilter } from "./Filters/InputFilter";
|
||||||
|
import { DateFilter } from "./Filters/DateFilter";
|
||||||
|
import { TimestampFilter } from "./Filters/TimestampFilter";
|
||||||
import { CountryCriterion } from "src/models/list-filter/criteria/country";
|
import { CountryCriterion } from "src/models/list-filter/criteria/country";
|
||||||
import { CountrySelect } from "../Shared";
|
import { CountrySelect } from "../Shared";
|
||||||
|
|
||||||
@@ -152,6 +158,8 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
|
|||||||
options &&
|
options &&
|
||||||
!criterionIsHierarchicalLabelValue(criterion.value) &&
|
!criterionIsHierarchicalLabelValue(criterion.value) &&
|
||||||
!criterionIsNumberValue(criterion.value) &&
|
!criterionIsNumberValue(criterion.value) &&
|
||||||
|
!criterionIsDateValue(criterion.value) &&
|
||||||
|
!criterionIsTimestampValue(criterion.value) &&
|
||||||
!Array.isArray(criterion.value)
|
!Array.isArray(criterion.value)
|
||||||
) {
|
) {
|
||||||
defaultValue.current = criterion.value;
|
defaultValue.current = criterion.value;
|
||||||
@@ -170,6 +178,19 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (criterion instanceof DateCriterion) {
|
||||||
|
return (
|
||||||
|
<DateFilter criterion={criterion} onValueChanged={onValueChanged} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (criterion instanceof TimestampCriterion) {
|
||||||
|
return (
|
||||||
|
<TimestampFilter
|
||||||
|
criterion={criterion}
|
||||||
|
onValueChanged={onValueChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (criterion instanceof NumberCriterion) {
|
if (criterion instanceof NumberCriterion) {
|
||||||
return (
|
return (
|
||||||
<NumberFilter criterion={criterion} onValueChanged={onValueChanged} />
|
<NumberFilter criterion={criterion} onValueChanged={onValueChanged} />
|
||||||
|
|||||||
121
ui/v2.5/src/components/List/Filters/DateFilter.tsx
Normal file
121
ui/v2.5/src/components/List/Filters/DateFilter.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import React, { useRef } from "react";
|
||||||
|
import { Form } from "react-bootstrap";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
|
import { IDateValue } from "../../../models/list-filter/types";
|
||||||
|
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
||||||
|
|
||||||
|
interface IDateFilterProps {
|
||||||
|
criterion: Criterion<IDateValue>;
|
||||||
|
onValueChanged: (value: IDateValue) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DateFilter: React.FC<IDateFilterProps> = ({
|
||||||
|
criterion,
|
||||||
|
onValueChanged,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const valueStage = useRef<IDateValue>(criterion.value);
|
||||||
|
|
||||||
|
function onChanged(
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
property: "value" | "value2"
|
||||||
|
) {
|
||||||
|
const { value } = event.target;
|
||||||
|
valueStage.current[property] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlurInput() {
|
||||||
|
onValueChanged(valueStage.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
let equalsControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.Equals ||
|
||||||
|
criterion.modifier === CriterionModifier.NotEquals
|
||||||
|
) {
|
||||||
|
equalsControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(e, "value")
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={criterion.value?.value ?? ""}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.value" }) + " (YYYY-MM-DD)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lowerControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.GreaterThan ||
|
||||||
|
criterion.modifier === CriterionModifier.Between ||
|
||||||
|
criterion.modifier === CriterionModifier.NotBetween
|
||||||
|
) {
|
||||||
|
lowerControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(e, "value")
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={criterion.value?.value ?? ""}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.greater_than" }) +
|
||||||
|
" (YYYY-MM-DD)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let upperControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.LessThan ||
|
||||||
|
criterion.modifier === CriterionModifier.Between ||
|
||||||
|
criterion.modifier === CriterionModifier.NotBetween
|
||||||
|
) {
|
||||||
|
upperControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(
|
||||||
|
e,
|
||||||
|
criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? "value"
|
||||||
|
: "value2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={
|
||||||
|
(criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? criterion.value?.value
|
||||||
|
: criterion.value?.value2) ?? ""
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.less_than" }) + " (YYYY-MM-DD)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{equalsControl}
|
||||||
|
{lowerControl}
|
||||||
|
{upperControl}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
123
ui/v2.5/src/components/List/Filters/TimestampFilter.tsx
Normal file
123
ui/v2.5/src/components/List/Filters/TimestampFilter.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React, { useRef } from "react";
|
||||||
|
import { Form } from "react-bootstrap";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
|
import { ITimestampValue } from "../../../models/list-filter/types";
|
||||||
|
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
||||||
|
|
||||||
|
interface ITimestampFilterProps {
|
||||||
|
criterion: Criterion<ITimestampValue>;
|
||||||
|
onValueChanged: (value: ITimestampValue) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TimestampFilter: React.FC<ITimestampFilterProps> = ({
|
||||||
|
criterion,
|
||||||
|
onValueChanged,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const valueStage = useRef<ITimestampValue>(criterion.value);
|
||||||
|
|
||||||
|
function onChanged(
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
property: "value" | "value2"
|
||||||
|
) {
|
||||||
|
const { value } = event.target;
|
||||||
|
valueStage.current[property] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlurInput() {
|
||||||
|
onValueChanged(valueStage.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
let equalsControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.Equals ||
|
||||||
|
criterion.modifier === CriterionModifier.NotEquals
|
||||||
|
) {
|
||||||
|
equalsControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(e, "value")
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={criterion.value?.value ?? ""}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.value" }) +
|
||||||
|
" (YYYY-MM-DD HH-MM)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lowerControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.GreaterThan ||
|
||||||
|
criterion.modifier === CriterionModifier.Between ||
|
||||||
|
criterion.modifier === CriterionModifier.NotBetween
|
||||||
|
) {
|
||||||
|
lowerControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(e, "value")
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={criterion.value?.value ?? ""}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.greater_than" }) +
|
||||||
|
" (YYYY-MM-DD HH-MM)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let upperControl: JSX.Element | null = null;
|
||||||
|
if (
|
||||||
|
criterion.modifier === CriterionModifier.LessThan ||
|
||||||
|
criterion.modifier === CriterionModifier.Between ||
|
||||||
|
criterion.modifier === CriterionModifier.NotBetween
|
||||||
|
) {
|
||||||
|
upperControl = (
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
className="btn-secondary"
|
||||||
|
type="text"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onChanged(
|
||||||
|
e,
|
||||||
|
criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? "value"
|
||||||
|
: "value2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onBlur={onBlurInput}
|
||||||
|
defaultValue={
|
||||||
|
(criterion.modifier === CriterionModifier.LessThan
|
||||||
|
? criterion.value?.value
|
||||||
|
: criterion.value?.value2) ?? ""
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
intl.formatMessage({ id: "criterion.less_than" }) +
|
||||||
|
" (YYYY-MM-DD HH-MM)"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{equalsControl}
|
||||||
|
{lowerControl}
|
||||||
|
{upperControl}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
### ✨ New Features
|
### ✨ New Features
|
||||||
|
* Added filter criteria for Birthdate, Death Date, Date, Created At and Updated At fields. ([#2834](https://github.com/stashapp/stash/pull/2834))
|
||||||
* Support creation of scenes without files. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
* Support creation of scenes without files. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
||||||
* Added ability to reassign files to other scenes. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
* Added ability to reassign files to other scenes. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
||||||
* Added ability to split and merge scenes. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
* Added ability to split and merge scenes. ([#3006](https://github.com/stashapp/stash/pull/3006))
|
||||||
|
|||||||
@@ -930,6 +930,9 @@
|
|||||||
"release_notes": "Release Notes",
|
"release_notes": "Release Notes",
|
||||||
"resolution": "Resolution",
|
"resolution": "Resolution",
|
||||||
"scene": "Scene",
|
"scene": "Scene",
|
||||||
|
"scene_date": "Date of Scene",
|
||||||
|
"scene_created_at": "Scene Created At",
|
||||||
|
"scene_updated_at": "Scene Updated At",
|
||||||
"sceneTagger": "Scene Tagger",
|
"sceneTagger": "Scene Tagger",
|
||||||
"sceneTags": "Scene Tags",
|
"sceneTags": "Scene Tags",
|
||||||
"scene_code": "Studio Code",
|
"scene_code": "Studio Code",
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
IntCriterionInput,
|
IntCriterionInput,
|
||||||
MultiCriterionInput,
|
MultiCriterionInput,
|
||||||
PHashDuplicationCriterionInput,
|
PHashDuplicationCriterionInput,
|
||||||
|
DateCriterionInput,
|
||||||
|
TimestampCriterionInput,
|
||||||
} from "src/core/generated-graphql";
|
} from "src/core/generated-graphql";
|
||||||
import DurationUtils from "src/utils/duration";
|
import DurationUtils from "src/utils/duration";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +19,8 @@ import {
|
|||||||
ILabeledValue,
|
ILabeledValue,
|
||||||
INumberValue,
|
INumberValue,
|
||||||
IOptionType,
|
IOptionType,
|
||||||
|
IDateValue,
|
||||||
|
ITimestampValue,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
export type Option = string | number | IOptionType;
|
export type Option = string | number | IOptionType;
|
||||||
@@ -24,7 +28,9 @@ export type CriterionValue =
|
|||||||
| string
|
| string
|
||||||
| ILabeledId[]
|
| ILabeledId[]
|
||||||
| IHierarchicalLabelValue
|
| IHierarchicalLabelValue
|
||||||
| INumberValue;
|
| INumberValue
|
||||||
|
| IDateValue
|
||||||
|
| ITimestampValue;
|
||||||
|
|
||||||
const modifierMessageIDs = {
|
const modifierMessageIDs = {
|
||||||
[CriterionModifier.Equals]: "criterion_modifier.equals",
|
[CriterionModifier.Equals]: "criterion_modifier.equals",
|
||||||
@@ -501,3 +507,162 @@ export class PhashDuplicateCriterion extends StringCriterion {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DateCriterionOption extends CriterionOption {
|
||||||
|
constructor(
|
||||||
|
messageID: string,
|
||||||
|
value: CriterionType,
|
||||||
|
parameterName?: string,
|
||||||
|
options?: Option[]
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
messageID,
|
||||||
|
type: value,
|
||||||
|
parameterName,
|
||||||
|
modifierOptions: [
|
||||||
|
CriterionModifier.Equals,
|
||||||
|
CriterionModifier.NotEquals,
|
||||||
|
CriterionModifier.GreaterThan,
|
||||||
|
CriterionModifier.LessThan,
|
||||||
|
CriterionModifier.IsNull,
|
||||||
|
CriterionModifier.NotNull,
|
||||||
|
CriterionModifier.Between,
|
||||||
|
CriterionModifier.NotBetween,
|
||||||
|
],
|
||||||
|
defaultModifier: CriterionModifier.Equals,
|
||||||
|
options,
|
||||||
|
inputType: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDateCriterionOption(value: CriterionType) {
|
||||||
|
return new DateCriterionOption(value, value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DateCriterion extends Criterion<IDateValue> {
|
||||||
|
public encodeValue() {
|
||||||
|
return {
|
||||||
|
value: this.value.value,
|
||||||
|
value2: this.value.value2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): DateCriterionInput {
|
||||||
|
return {
|
||||||
|
modifier: this.modifier,
|
||||||
|
value: this.value.value,
|
||||||
|
value2: this.value.value2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLabelValue() {
|
||||||
|
const { value } = this.value;
|
||||||
|
return this.modifier === CriterionModifier.Between ||
|
||||||
|
this.modifier === CriterionModifier.NotBetween
|
||||||
|
? `${value}, ${this.value.value2}`
|
||||||
|
: `${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: CriterionOption) {
|
||||||
|
super(type, { value: "", value2: undefined });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TimestampCriterionOption extends CriterionOption {
|
||||||
|
constructor(
|
||||||
|
messageID: string,
|
||||||
|
value: CriterionType,
|
||||||
|
parameterName?: string,
|
||||||
|
options?: Option[]
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
messageID,
|
||||||
|
type: value,
|
||||||
|
parameterName,
|
||||||
|
modifierOptions: [
|
||||||
|
CriterionModifier.GreaterThan,
|
||||||
|
CriterionModifier.LessThan,
|
||||||
|
CriterionModifier.IsNull,
|
||||||
|
CriterionModifier.NotNull,
|
||||||
|
CriterionModifier.Between,
|
||||||
|
CriterionModifier.NotBetween,
|
||||||
|
],
|
||||||
|
defaultModifier: CriterionModifier.GreaterThan,
|
||||||
|
options,
|
||||||
|
inputType: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTimestampCriterionOption(value: CriterionType) {
|
||||||
|
return new TimestampCriterionOption(value, value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TimestampCriterion extends Criterion<ITimestampValue> {
|
||||||
|
public encodeValue() {
|
||||||
|
return {
|
||||||
|
value: this.value.value,
|
||||||
|
value2: this.value.value2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): TimestampCriterionInput {
|
||||||
|
return {
|
||||||
|
modifier: this.modifier,
|
||||||
|
value: this.transformValueToInput(this.value.value),
|
||||||
|
value2: this.value.value2
|
||||||
|
? this.transformValueToInput(this.value.value2)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLabelValue() {
|
||||||
|
const { value } = this.value;
|
||||||
|
return this.modifier === CriterionModifier.Between ||
|
||||||
|
this.modifier === CriterionModifier.NotBetween
|
||||||
|
? `${value}, ${this.value.value2}`
|
||||||
|
: `${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformValueToInput(value: string): string {
|
||||||
|
value = value.trim();
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}(( |T)\d{2}:\d{2})?$/.test(value)) {
|
||||||
|
return value.replace(" ", "T");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: CriterionOption) {
|
||||||
|
super(type, { value: "", value2: undefined });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MandatoryTimestampCriterionOption extends CriterionOption {
|
||||||
|
constructor(
|
||||||
|
messageID: string,
|
||||||
|
value: CriterionType,
|
||||||
|
parameterName?: string,
|
||||||
|
options?: Option[]
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
messageID,
|
||||||
|
type: value,
|
||||||
|
parameterName,
|
||||||
|
modifierOptions: [
|
||||||
|
CriterionModifier.GreaterThan,
|
||||||
|
CriterionModifier.LessThan,
|
||||||
|
CriterionModifier.Between,
|
||||||
|
CriterionModifier.NotBetween,
|
||||||
|
],
|
||||||
|
defaultModifier: CriterionModifier.GreaterThan,
|
||||||
|
options,
|
||||||
|
inputType: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMandatoryTimestampCriterionOption(value: CriterionType) {
|
||||||
|
return new MandatoryTimestampCriterionOption(value, value, value);
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import {
|
|||||||
ILabeledIdCriterion,
|
ILabeledIdCriterion,
|
||||||
BooleanCriterion,
|
BooleanCriterion,
|
||||||
BooleanCriterionOption,
|
BooleanCriterionOption,
|
||||||
|
DateCriterion,
|
||||||
|
DateCriterionOption,
|
||||||
|
TimestampCriterion,
|
||||||
|
MandatoryTimestampCriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
import { OrganizedCriterion } from "./organized";
|
import { OrganizedCriterion } from "./organized";
|
||||||
import { FavoriteCriterion, PerformerFavoriteCriterion } from "./favorite";
|
import { FavoriteCriterion, PerformerFavoriteCriterion } from "./favorite";
|
||||||
@@ -193,5 +197,17 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
);
|
);
|
||||||
case "ignore_auto_tag":
|
case "ignore_auto_tag":
|
||||||
return new BooleanCriterion(new BooleanCriterionOption(type, type));
|
return new BooleanCriterion(new BooleanCriterionOption(type, type));
|
||||||
|
case "date":
|
||||||
|
case "birthdate":
|
||||||
|
case "death_date":
|
||||||
|
case "scene_date":
|
||||||
|
return new DateCriterion(new DateCriterionOption(type, type));
|
||||||
|
case "created_at":
|
||||||
|
case "updated_at":
|
||||||
|
case "scene_created_at":
|
||||||
|
case "scene_updated_at":
|
||||||
|
return new TimestampCriterion(
|
||||||
|
new MandatoryTimestampCriterionOption(type, type)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
|
createDateCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||||
import { GalleryIsMissingCriterionOption } from "./criteria/is-missing";
|
import { GalleryIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
@@ -61,6 +63,9 @@ const criterionOptions = [
|
|||||||
StudiosCriterionOption,
|
StudiosCriterionOption,
|
||||||
createStringCriterionOption("url"),
|
createStringCriterionOption("url"),
|
||||||
createMandatoryNumberCriterionOption("file_count", "zip_file_count"),
|
createMandatoryNumberCriterionOption("file_count", "zip_file_count"),
|
||||||
|
createDateCriterionOption("date"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const GalleryListFilterOptions = new ListFilterOptions(
|
export const GalleryListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createMandatoryStringCriterionOption,
|
createMandatoryStringCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||||
import { ImageIsMissingCriterionOption } from "./criteria/is-missing";
|
import { ImageIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
@@ -45,6 +46,8 @@ const criterionOptions = [
|
|||||||
PerformerFavoriteCriterionOption,
|
PerformerFavoriteCriterionOption,
|
||||||
StudiosCriterionOption,
|
StudiosCriterionOption,
|
||||||
createMandatoryNumberCriterionOption("file_count"),
|
createMandatoryNumberCriterionOption("file_count"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
export const ImageListFilterOptions = new ListFilterOptions(
|
export const ImageListFilterOptions = new ListFilterOptions(
|
||||||
defaultSortBy,
|
defaultSortBy,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
|
createDateCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { MovieIsMissingCriterionOption } from "./criteria/is-missing";
|
import { MovieIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
import { RatingCriterionOption } from "./criteria/rating";
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
@@ -30,6 +32,9 @@ const criterionOptions = [
|
|||||||
createMandatoryNumberCriterionOption("duration"),
|
createMandatoryNumberCriterionOption("duration"),
|
||||||
RatingCriterionOption,
|
RatingCriterionOption,
|
||||||
PerformersCriterionOption,
|
PerformersCriterionOption,
|
||||||
|
createDateCriterionOption("date"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const MovieListFilterOptions = new ListFilterOptions(
|
export const MovieListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import {
|
|||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
createBooleanCriterionOption,
|
createBooleanCriterionOption,
|
||||||
|
createDateCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
NumberCriterionOption,
|
NumberCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { FavoriteCriterionOption } from "./criteria/favorite";
|
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||||
@@ -84,6 +86,10 @@ const criterionOptions = [
|
|||||||
new NumberCriterionOption("height", "height_cm", "height_cm"),
|
new NumberCriterionOption("height", "height_cm", "height_cm"),
|
||||||
...numberCriteria.map((c) => createNumberCriterionOption(c)),
|
...numberCriteria.map((c) => createNumberCriterionOption(c)),
|
||||||
...stringCriteria.map((c) => createStringCriterionOption(c)),
|
...stringCriteria.map((c) => createStringCriterionOption(c)),
|
||||||
|
createDateCriterionOption("birthdate"),
|
||||||
|
createDateCriterionOption("death_date"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
export const PerformerListFilterOptions = new ListFilterOptions(
|
export const PerformerListFilterOptions = new ListFilterOptions(
|
||||||
defaultSortBy,
|
defaultSortBy,
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { PerformersCriterionOption } from "./criteria/performers";
|
|||||||
import { SceneTagsCriterionOption, TagsCriterionOption } from "./criteria/tags";
|
import { SceneTagsCriterionOption, TagsCriterionOption } from "./criteria/tags";
|
||||||
import { ListFilterOptions } from "./filter-options";
|
import { ListFilterOptions } from "./filter-options";
|
||||||
import { DisplayMode } from "./types";
|
import { DisplayMode } from "./types";
|
||||||
|
import {
|
||||||
|
createDateCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
|
} from "./criteria/criterion";
|
||||||
|
|
||||||
const defaultSortBy = "title";
|
const defaultSortBy = "title";
|
||||||
const sortByOptions = [
|
const sortByOptions = [
|
||||||
@@ -16,6 +20,11 @@ const criterionOptions = [
|
|||||||
TagsCriterionOption,
|
TagsCriterionOption,
|
||||||
SceneTagsCriterionOption,
|
SceneTagsCriterionOption,
|
||||||
PerformersCriterionOption,
|
PerformersCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
|
createDateCriterionOption("scene_date"),
|
||||||
|
createMandatoryTimestampCriterionOption("scene_created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("scene_updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SceneMarkerListFilterOptions = new ListFilterOptions(
|
export const SceneMarkerListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import {
|
|||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createMandatoryStringCriterionOption,
|
createMandatoryStringCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
|
createDateCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { HasMarkersCriterionOption } from "./criteria/has-markers";
|
import { HasMarkersCriterionOption } from "./criteria/has-markers";
|
||||||
import { SceneIsMissingCriterionOption } from "./criteria/is-missing";
|
import { SceneIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
@@ -85,6 +87,9 @@ const criterionOptions = [
|
|||||||
CaptionsCriterionOption,
|
CaptionsCriterionOption,
|
||||||
createMandatoryNumberCriterionOption("interactive_speed"),
|
createMandatoryNumberCriterionOption("interactive_speed"),
|
||||||
createMandatoryNumberCriterionOption("file_count"),
|
createMandatoryNumberCriterionOption("file_count"),
|
||||||
|
createDateCriterionOption("date"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SceneListFilterOptions = new ListFilterOptions(
|
export const SceneListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createMandatoryNumberCriterionOption,
|
createMandatoryNumberCriterionOption,
|
||||||
createMandatoryStringCriterionOption,
|
createMandatoryStringCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
|
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
import { RatingCriterionOption } from "./criteria/rating";
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
@@ -42,6 +43,8 @@ const criterionOptions = [
|
|||||||
createStringCriterionOption("url"),
|
createStringCriterionOption("url"),
|
||||||
createStringCriterionOption("stash_id"),
|
createStringCriterionOption("stash_id"),
|
||||||
createStringCriterionOption("aliases"),
|
createStringCriterionOption("aliases"),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const StudioListFilterOptions = new ListFilterOptions(
|
export const StudioListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
createMandatoryStringCriterionOption,
|
createMandatoryStringCriterionOption,
|
||||||
createStringCriterionOption,
|
createStringCriterionOption,
|
||||||
MandatoryNumberCriterionOption,
|
MandatoryNumberCriterionOption,
|
||||||
|
createMandatoryTimestampCriterionOption,
|
||||||
} from "./criteria/criterion";
|
} from "./criteria/criterion";
|
||||||
import { TagIsMissingCriterionOption } from "./criteria/is-missing";
|
import { TagIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
import { ListFilterOptions } from "./filter-options";
|
import { ListFilterOptions } from "./filter-options";
|
||||||
@@ -63,6 +64,8 @@ const criterionOptions = [
|
|||||||
"child_tag_count",
|
"child_tag_count",
|
||||||
"child_count"
|
"child_count"
|
||||||
),
|
),
|
||||||
|
createMandatoryTimestampCriterionOption("created_at"),
|
||||||
|
createMandatoryTimestampCriterionOption("updated_at"),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const TagListFilterOptions = new ListFilterOptions(
|
export const TagListFilterOptions = new ListFilterOptions(
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ export interface IPHashDuplicationValue {
|
|||||||
distance?: number; // currently not implemented
|
distance?: number; // currently not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDateValue {
|
||||||
|
value: string;
|
||||||
|
value2: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITimestampValue {
|
||||||
|
value: string;
|
||||||
|
value2: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function criterionIsHierarchicalLabelValue(
|
export function criterionIsHierarchicalLabelValue(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
value: any
|
value: any
|
||||||
@@ -47,6 +57,20 @@ export function criterionIsNumberValue(
|
|||||||
return typeof value === "object" && "value" in value && "value2" in value;
|
return typeof value === "object" && "value" in value && "value2" in value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function criterionIsDateValue(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
value: any
|
||||||
|
): value is IDateValue {
|
||||||
|
return typeof value === "object" && "value" in value && "value2" in value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function criterionIsTimestampValue(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
value: any
|
||||||
|
): value is ITimestampValue {
|
||||||
|
return typeof value === "object" && "value" in value && "value2" in value;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IOptionType {
|
export interface IOptionType {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -126,5 +150,13 @@ export type CriterionType =
|
|||||||
| "duplicated"
|
| "duplicated"
|
||||||
| "ignore_auto_tag"
|
| "ignore_auto_tag"
|
||||||
| "file_count"
|
| "file_count"
|
||||||
|
| "date"
|
||||||
|
| "created_at"
|
||||||
|
| "updated_at"
|
||||||
|
| "birthdate"
|
||||||
|
| "death_date"
|
||||||
|
| "scene_date"
|
||||||
|
| "scene_created_at"
|
||||||
|
| "scene_updated_at"
|
||||||
| "description"
|
| "description"
|
||||||
| "scene_code";
|
| "scene_code";
|
||||||
|
|||||||
Reference in New Issue
Block a user