Skip to content

Commit 7291c1d

Browse files
committed
Search: Add landscape/square filters, and "show filters" command #2169
1 parent 0427163 commit 7291c1d

17 files changed

+415
-116
lines changed

internal/commands/show.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var ShowCommand = cli.Command{
1010
Usage: "Configuration and system report subcommands",
1111
Subcommands: []cli.Command{
1212
ShowConfigCommand,
13+
ShowFiltersCommand,
1314
ShowFormatsCommand,
1415
},
1516
}

internal/commands/show_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
var ShowConfigCommand = cli.Command{
1414
Name: "config",
15-
Usage: "Displays global configuration values",
15+
Usage: "Shows global configuration values",
1616
Flags: []cli.Flag{
1717
cli.BoolFlag{
1818
Name: "no-wrap, n",

internal/commands/show_config_test.go

Lines changed: 0 additions & 32 deletions
This file was deleted.

internal/commands/show_filters.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/urfave/cli"
8+
9+
"github.com/photoprism/photoprism/internal/form"
10+
"github.com/photoprism/photoprism/pkg/report"
11+
)
12+
13+
var ShowFiltersCommand = cli.Command{
14+
Name: "filters",
15+
Usage: "Displays a search filter overview with examples",
16+
Flags: []cli.Flag{
17+
cli.BoolFlag{
18+
Name: "no-wrap, n",
19+
Usage: "disable text-wrapping so the output can be pasted into Markdown files",
20+
},
21+
},
22+
Action: showFiltersAction,
23+
}
24+
25+
// showFiltersAction lists supported search filters.
26+
func showFiltersAction(ctx *cli.Context) error {
27+
rows, cols := form.Table(&form.SearchPhotos{})
28+
29+
sort.Slice(rows, func(i, j int) bool {
30+
if rows[i][1] == rows[j][1] {
31+
return rows[i][0] < rows[j][0]
32+
} else {
33+
return rows[i][1] < rows[j][1]
34+
}
35+
})
36+
37+
fmt.Println(report.Markdown(rows, cols, !ctx.Bool("no-wrap")))
38+
39+
return nil
40+
}

internal/commands/show_formats.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
var ShowFormatsCommand = cli.Command{
1313
Name: "formats",
14-
Usage: "Displays supported media and sidecar file formats",
14+
Usage: "Lists supported media and sidecar file formats",
1515
Flags: []cli.Flag{
1616
cli.BoolFlag{
1717
Name: "compact, c",

internal/commands/show_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package commands
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
"github.com/photoprism/photoprism/internal/config"
9+
"github.com/photoprism/photoprism/pkg/capture"
10+
)
11+
12+
func TestShowConfigCommand(t *testing.T) {
13+
var err error
14+
15+
ctx := config.CliTestContext()
16+
17+
output := capture.Output(func() {
18+
err = ShowConfigCommand.Run(ctx)
19+
})
20+
21+
if err != nil {
22+
t.Fatal(err)
23+
}
24+
25+
// Expected config command output.
26+
assert.Contains(t, output, "config-file")
27+
assert.Contains(t, output, "darktable-cli")
28+
assert.Contains(t, output, "originals-path")
29+
assert.Contains(t, output, "import-path")
30+
assert.Contains(t, output, "cache-path")
31+
assert.Contains(t, output, "assets-path")
32+
}
33+
34+
func TestShowFiltersCommand(t *testing.T) {
35+
var err error
36+
37+
ctx := config.CliTestContext()
38+
39+
output := capture.Output(func() {
40+
err = ShowFiltersCommand.Run(ctx)
41+
})
42+
43+
if err != nil {
44+
t.Fatal(err)
45+
}
46+
47+
// Expected config command output.
48+
assert.Contains(t, output, "landscape")
49+
assert.Contains(t, output, "live")
50+
assert.Contains(t, output, "Examples")
51+
assert.Contains(t, output, "Filter")
52+
assert.Contains(t, output, "Notes")
53+
}
54+
55+
func TestShowFormatsCommand(t *testing.T) {
56+
var err error
57+
58+
ctx := config.CliTestContext()
59+
60+
output := capture.Output(func() {
61+
err = ShowFormatsCommand.Run(ctx)
62+
})
63+
64+
if err != nil {
65+
t.Fatal(err)
66+
}
67+
68+
// Expected config command output.
69+
assert.Contains(t, output, "JPEG")
70+
assert.Contains(t, output, "MP4")
71+
assert.Contains(t, output, "Image")
72+
assert.Contains(t, output, "Format")
73+
assert.Contains(t, output, "Description")
74+
}

internal/entity/file_fixtures.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ var FileFixtures = FileMap{
133133
FileHeight: 0,
134134
FileOrientation: 0,
135135
FileProjection: "",
136-
FileAspectRatio: 0,
136+
FileAspectRatio: 1,
137137
FileMainColor: "",
138138
FileColors: "",
139139
FileLuminance: "",
@@ -1562,11 +1562,11 @@ var FileFixtures = FileMap{
15621562
FileMissing: false,
15631563
FilePortrait: false,
15641564
FileDuration: 0,
1565-
FileWidth: 640,
1566-
FileHeight: 1136,
1565+
FileWidth: 16000,
1566+
FileHeight: 16000,
15671567
FileOrientation: 1,
15681568
FileProjection: "",
1569-
FileAspectRatio: 0.56,
1569+
FileAspectRatio: 1,
15701570
FileMainColor: "grey",
15711571
FileColors: "141101110",
15721572
FileLuminance: "BD9A22751",

internal/form/search_geojson.go

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,55 @@ import "time"
44

55
// SearchGeo represents search form fields for "/api/v1/geo".
66
type SearchGeo struct {
7-
Query string `form:"q"`
8-
Filter string `form:"filter"`
9-
Near string `form:"near"`
10-
Type string `form:"type"`
11-
Path string `form:"path"`
12-
Folder string `form:"folder"` // Alias for Path
13-
Name string `form:"name"`
14-
Title string `form:"title"`
15-
Before time.Time `form:"before" time_format:"2006-01-02"`
16-
After time.Time `form:"after" time_format:"2006-01-02"`
17-
Favorite bool `form:"favorite"`
18-
Unsorted bool `form:"unsorted"`
19-
Video bool `form:"video"`
20-
Photo bool `form:"photo"`
21-
Raw bool `form:"raw"`
22-
Live bool `form:"live"`
23-
Scan bool `form:"scan"`
24-
Panorama bool `form:"panorama"`
25-
Archived bool `form:"archived"`
26-
Public bool `form:"public"`
27-
Private bool `form:"private"`
28-
Review bool `form:"review"`
29-
Quality int `form:"quality"`
30-
Faces string `form:"faces"` // Find or exclude faces if detected.
31-
Lat float32 `form:"lat"`
32-
Lng float32 `form:"lng"`
33-
S2 string `form:"s2"`
34-
Olc string `form:"olc"`
35-
Dist uint `form:"dist"`
36-
Face string `form:"face"` // UIDs
37-
Subject string `form:"subject"` // UIDs
38-
Person string `form:"person"` // Alias for Subject
39-
Subjects string `form:"subjects"` // Text
40-
People string `form:"people"` // Alias for Subjects
41-
Keywords string `form:"keywords"`
42-
Album string `form:"album"`
43-
Albums string `form:"albums"`
44-
Country string `form:"country"`
45-
Year string `form:"year"` // Moments
46-
Month string `form:"month"` // Moments
47-
Day string `form:"day"` // Moments
48-
Color string `form:"color"`
49-
Camera int `form:"camera"`
50-
Lens int `form:"lens"`
51-
Count int `form:"count" serialize:"-"`
52-
Offset int `form:"offset" serialize:"-"`
7+
Query string `form:"q"`
8+
Filter string `form:"filter"`
9+
Near string `form:"near"`
10+
Type string `form:"type"`
11+
Path string `form:"path"`
12+
Folder string `form:"folder"` // Alias for Path
13+
Name string `form:"name"`
14+
Title string `form:"title"`
15+
Before time.Time `form:"before" time_format:"2006-01-02"`
16+
After time.Time `form:"after" time_format:"2006-01-02"`
17+
Favorite bool `form:"favorite"`
18+
Unsorted bool `form:"unsorted"`
19+
Video bool `form:"video"`
20+
Photo bool `form:"photo"`
21+
Raw bool `form:"raw"`
22+
Live bool `form:"live"`
23+
Scan bool `form:"scan"`
24+
Panorama bool `form:"panorama"`
25+
Portrait bool `form:"portrait"`
26+
Landscape bool `form:"landscape"`
27+
Square bool `form:"square"`
28+
Archived bool `form:"archived"`
29+
Public bool `form:"public"`
30+
Private bool `form:"private"`
31+
Review bool `form:"review"`
32+
Quality int `form:"quality"`
33+
Faces string `form:"faces"` // Find or exclude faces if detected.
34+
Lat float32 `form:"lat"`
35+
Lng float32 `form:"lng"`
36+
S2 string `form:"s2"`
37+
Olc string `form:"olc"`
38+
Dist uint `form:"dist"`
39+
Face string `form:"face"` // UIDs
40+
Subject string `form:"subject"` // UIDs
41+
Person string `form:"person"` // Alias for Subject
42+
Subjects string `form:"subjects"` // Text
43+
People string `form:"people"` // Alias for Subjects
44+
Keywords string `form:"keywords"`
45+
Album string `form:"album"`
46+
Albums string `form:"albums"`
47+
Country string `form:"country"`
48+
Year string `form:"year"` // Moments
49+
Month string `form:"month"` // Moments
50+
Day string `form:"day"` // Moments
51+
Color string `form:"color"`
52+
Camera int `form:"camera"`
53+
Lens int `form:"lens"`
54+
Count int `form:"count" serialize:"-"`
55+
Offset int `form:"offset" serialize:"-"`
5356
}
5457

5558
// GetQuery returns the query parameter as string.

internal/form/search_geojson_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,25 @@ func TestGeoSearch(t *testing.T) {
7979
assert.Equal(t, uint(0x61a8), form.Dist)
8080
assert.Equal(t, float32(33.45343), form.Lat)
8181
})
82+
t.Run("PortraitLandscapeSquare", func(t *testing.T) {
83+
form := &SearchGeo{Query: "portrait:true landscape:yes square:jo"}
84+
85+
assert.False(t, form.Portrait)
86+
assert.False(t, form.Landscape)
87+
assert.False(t, form.Square)
88+
assert.False(t, form.Panorama)
89+
90+
err := form.ParseQueryString()
91+
92+
if err != nil {
93+
t.Fatal(err)
94+
}
95+
96+
assert.True(t, form.Portrait)
97+
assert.True(t, form.Landscape)
98+
assert.True(t, form.Square)
99+
assert.False(t, form.Panorama)
100+
})
82101
}
83102

84103
func TestGeoSearch_Serialize(t *testing.T) {

0 commit comments

Comments
 (0)