Experimental filters for category and country (photo search)

This commit is contained in:
Michael Mayer 2018-09-19 00:53:39 +02:00
parent 39ab854672
commit d3ef7abb54
9 changed files with 74 additions and 47 deletions

View file

@ -18,7 +18,8 @@
appName: "{{ .title }}", appName: "{{ .title }}",
appVersion: "1.0.0", appVersion: "1.0.0",
debug: {{ .debug }}, debug: {{ .debug }},
cameras: {{ .cameras }} cameras: {{ .cameras }},
countries: {{ .countries }}
}; };
</script> </script>
</head> </head>

View file

@ -40,6 +40,8 @@
label="Country" label="Country"
flat solo flat solo
color="blue-grey" color="blue-grey"
item-value="LocCountryCode"
item-text="LocCountry"
v-model="query.country" v-model="query.country"
:items="options.countries"> :items="options.countries">
</v-select> </v-select>
@ -77,6 +79,7 @@
direction="top" direction="top"
open-on-hover open-on-hover
transition="slide-y-reverse-transition" transition="slide-y-reverse-transition"
style="right: 8px; bottom: 8px;"
> >
<v-btn <v-btn
slot="activator" slot="activator"
@ -204,6 +207,10 @@
const country = query['country'] ? query['country'] : ''; const country = query['country'] ? query['country'] : '';
const view = query['view'] ? query['view'] : 'tile'; const view = query['view'] ? query['view'] : 'tile';
const cameras = [{ID: 0, CameraModel: 'All Cameras'}].concat(this.$config.getValue('cameras')); const cameras = [{ID: 0, CameraModel: 'All Cameras'}].concat(this.$config.getValue('cameras'));
const countries = [{
LocCountryCode: '',
LocCountry: 'All Countries'
}].concat(this.$config.getValue('countries'));
return { return {
'snackbarVisible': false, 'snackbarVisible': false,
@ -220,16 +227,14 @@
'options': { 'options': {
'categories': [ 'categories': [
{value: '', text: 'All Categories'}, {value: '', text: 'All Categories'},
{value: 'junction', text: 'Junction'}, {value: 'airport', text: 'Airport'},
{value: 'tourism', text: 'Tourism'}, {value: 'amenity', text: 'Amenity'},
{value: 'building', text: 'Building'},
{value: 'historic', text: 'Historic'}, {value: 'historic', text: 'Historic'},
{value: 'shop', text: 'Shop'},
{value: 'tourism', text: 'Tourism'},
], ],
'countries': [ 'countries': countries,
{value: '', text: 'All Countries'},
{value: 'de', text: 'Germany'},
{value: 'ca', text: 'Canada'},
{value: 'us', text: 'United States'}
],
'cameras': cameras, 'cameras': cameras,
'sorting': [ 'sorting': [
{value: 'newest', text: 'Newest first'}, {value: 'newest', text: 'Newest first'},

View file

@ -7,7 +7,7 @@ import (
type PhotoSearchForm struct { type PhotoSearchForm struct {
Query string `form:"q"` Query string `form:"q"`
Tags string `form:"tags"` Tags string `form:"tags"`
Category string `form:"cat"` Cat string `form:"cat"`
Country string `form:"country"` Country string `form:"country"`
CameraID int `form:"camera"` CameraID int `form:"camera"`
Order string `form:"order"` Order string `form:"order"`

View file

@ -231,7 +231,15 @@ func (c *Config) GetClientConfig() ConfigValues {
db := c.GetDb() db := c.GetDb()
var cameras []*Camera var cameras []*Camera
// var countries map[string]string
type country struct {
LocCountry string
LocCountryCode string
}
var countries []country
db.Model(&Location{}).Select("DISTINCT loc_country_code, loc_country").Scan(&countries)
db.Where("deleted_at IS NULL").Limit(1000).Order("camera_model").Find(&cameras) db.Where("deleted_at IS NULL").Limit(1000).Order("camera_model").Find(&cameras)
@ -242,6 +250,7 @@ func (c *Config) GetClientConfig() ConfigValues {
"title": "PhotoPrism", "title": "PhotoPrism",
"debug": c.Debug, "debug": c.Debug,
"cameras": cameras, "cameras": cameras,
"countries": countries,
"jsHash": jsHash, "jsHash": jsHash,
"cssHash": cssHash, "cssHash": cssHash,
} }

View file

@ -39,7 +39,7 @@ func (m *MediaFile) GetExifData() (*ExifData, error) {
file, err := m.openFile() file, err := m.openFile()
if err != nil { if err != nil {
return m.exifData, err return nil, err
} }
defer file.Close() defer file.Close()
@ -49,7 +49,7 @@ func (m *MediaFile) GetExifData() (*ExifData, error) {
x, err := exif.Decode(file) x, err := exif.Decode(file)
if err != nil { if err != nil {
return m.exifData, err return nil, err
} }
if artist, err := x.Get(exif.Artist); err == nil { if artist, err := x.Get(exif.Artist); err == nil {

View file

@ -77,7 +77,7 @@ func NewSearch(originalsPath string, db *gorm.DB) *Search {
return instance return instance
} }
func (s *Search) Photos(form PhotoSearchForm) ([]PhotoSearchResult, int, error) { func (s *Search) Photos(form PhotoSearchForm) ([]PhotoSearchResult, error) {
q := s.db.NewScope(nil).DB() q := s.db.NewScope(nil).DB()
q = q.Table("photos"). q = q.Table("photos").
Select(`SQL_CALC_FOUND_ROWS photos.*, Select(`SQL_CALC_FOUND_ROWS photos.*,
@ -98,7 +98,39 @@ func (s *Search) Photos(form PhotoSearchForm) ([]PhotoSearchResult, int, error)
} }
if form.CameraID > 0 { if form.CameraID > 0 {
q = q.Where("camera_id = ?", form.CameraID) q = q.Where("photos.camera_id = ?", form.CameraID)
}
if form.Country != "" {
q = q.Where("locations.loc_country_code = ?", form.Country)
}
switch form.Cat {
case "amenity":
q = q.Where("locations.loc_category = 'amenity'")
case "bank":
q = q.Where("locations.loc_type = 'bank'")
case "building":
q = q.Where("locations.loc_category = 'building'")
case "school":
q = q.Where("locations.loc_type = 'school'")
case "supermarket":
q = q.Where("locations.loc_type = 'supermarket'")
case "shop":
q = q.Where("locations.loc_category = 'shop'")
case "hotel":
q = q.Where("locations.loc_type = 'hotel'")
case "bar":
q = q.Where("locations.loc_type = 'bar'")
case "parking":
q = q.Where("locations.loc_type = 'parking'")
case "airport":
q = q.Where("locations.loc_category = 'aeroway'")
case "historic":
q = q.Where("locations.loc_category = 'historic'")
case "tourism":
q = q.Where("locations.loc_category = 'tourism'")
default:
} }
switch form.Order { switch form.Order {
@ -118,28 +150,13 @@ func (s *Search) Photos(form PhotoSearchForm) ([]PhotoSearchResult, int, error)
q = q.Limit(100).Offset(0) q = q.Limit(100).Offset(0)
} }
results := make([]PhotoSearchResult, 0, form.Count) var results []PhotoSearchResult
rows, err := q.Rows() if result := q.Scan(&results); result.Error != nil {
return results, result.Error
if err != nil {
return results, 0, err
} }
defer rows.Close() return results, nil
for rows.Next() {
var result PhotoSearchResult
s.db.ScanRows(rows, &result)
results = append(results, result)
}
// TODO: Check if this works properly with concurrent requests and caching
count := &SearchCount{}
s.db.Raw("SELECT FOUND_ROWS() AS total").Scan(&count)
total := count.Total
return results, total, nil
} }
func (s *Search) FindFiles(count int, offset int) (files []File) { func (s *Search) FindFiles(count int, offset int) (files []File) {

View file

@ -20,23 +20,21 @@ func TestSearch_Photos_Query(t *testing.T) {
form.Count = 3 form.Count = 3
form.Offset = 0 form.Offset = 0
photos, total, err := search.Photos(form) photos, err := search.Photos(form)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Log(photos) t.Log(photos)
t.Logf("Total Count: %d", total)
photos, total, err = search.Photos(form) photos, err = search.Photos(form)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Log(photos) t.Log(photos)
t.Logf("Total Count: %d", total)
} }
@ -56,12 +54,11 @@ func TestSearch_Photos_Camera(t *testing.T) {
form.Count = 3 form.Count = 3
form.Offset = 0 form.Offset = 0
photos, total, err := search.Photos(form) photos, err := search.Photos(form)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Log(photos) t.Log(photos)
t.Logf("Total Count: %d", total)
} }

View file

@ -25,7 +25,6 @@ func CreateThumbnailsFromOriginals(originalsPath string, thumbnailsPath string,
if thumbnail, err := mediaFile.GetSquareThumbnail(thumbnailsPath, size); err != nil { if thumbnail, err := mediaFile.GetSquareThumbnail(thumbnailsPath, size); err != nil {
log.Printf("Could not create thumbnail: %s", err.Error()) log.Printf("Could not create thumbnail: %s", err.Error())
} else { } else {
thumbnail.GetHeight()
log.Printf("Created %dx%d px thumbnail for \"%s\"", thumbnail.GetWidth(), thumbnail.GetHeight(), mediaFile.GetRelativeFilename(originalsPath)) log.Printf("Created %dx%d px thumbnail for \"%s\"", thumbnail.GetWidth(), thumbnail.GetHeight(), mediaFile.GetRelativeFilename(originalsPath))
} }
} else { } else {

View file

@ -28,13 +28,12 @@ func ConfigureRoutes(app *gin.Engine, conf *photoprism.Config) {
c.MustBindWith(&form, binding.Form) c.MustBindWith(&form, binding.Form)
result, total, err := search.Photos(form) result, err := search.Photos(form)
if err != nil { if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()}) c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
} }
c.Header("x-result-total", strconv.Itoa(total))
c.Header("x-result-count", strconv.Itoa(form.Count)) c.Header("x-result-count", strconv.Itoa(form.Count))
c.Header("x-result-offset", strconv.Itoa(form.Offset)) c.Header("x-result-offset", strconv.Itoa(form.Offset))