Places: Add support for new keywords field

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-04-28 19:41:06 +02:00
parent b33983b566
commit 96098913ff
8 changed files with 115 additions and 51 deletions

View file

@ -45,21 +45,22 @@ func (m *Location) Find(db *gorm.DB, api string) error {
return err
}
if place := FindPlaceByLabel(l.ID, l.LocLabel, db); place != nil {
if place := FindPlaceByLabel(l.S2Token(), l.Label(), db); place != nil {
m.Place = place
} else {
m.Place = &Place{
ID: l.ID,
LocLabel: l.LocLabel,
LocCity: l.LocCity,
LocState: l.LocState,
LocCountry: l.LocCountry,
ID: l.S2Token(),
LocLabel: l.Label(),
LocCity: l.City(),
LocState: l.State(),
LocCountry: l.CountryCode(),
LocKeywords: l.KeywordString(),
}
}
m.LocName = l.LocName
m.LocCategory = l.LocCategory
m.LocSource = l.LocSource
m.LocName = l.Name()
m.LocCategory = l.Category()
m.LocSource = l.Source()
if err := db.Create(m).Error; err == nil {
return nil
@ -72,11 +73,17 @@ func (m *Location) Find(db *gorm.DB, api string) error {
// Keywords computes keyword based on a Location
func (m *Location) Keywords() (result []string) {
if m.Place == nil {
log.Errorf("location: place for %s is nil - you might have found a bug", m.ID)
return result
}
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.City(), "-"))...)
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.State(), "-"))...)
result = append(result, txt.Keywords(txt.ReplaceSpaces(m.CountryName(), "-"))...)
result = append(result, txt.Keywords(m.Category())...)
result = append(result, txt.Keywords(m.Name())...)
result = append(result, txt.Keywords(m.Place.LocKeywords)...)
result = txt.UniqueWords(result)

View file

@ -11,10 +11,11 @@ import (
// Place used to associate photos to places
type Place struct {
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
LocLabel string `gorm:"type:varbinary(512);unique_index;"`
LocCity string `gorm:"type:varchar(128);"`
LocState string `gorm:"type:varchar(128);"`
LocLabel string `gorm:"type:varbinary(768);unique_index;"`
LocCity string `gorm:"type:varchar(255);"`
LocState string `gorm:"type:varchar(255);"`
LocCountry string `gorm:"type:varbinary(2);"`
LocKeywords string `gorm:"type:varchar(255);"`
LocNotes string `gorm:"type:text;"`
LocFavorite bool
CreatedAt time.Time
@ -24,11 +25,14 @@ type Place struct {
// UnknownPlace is defined here to use it as a default
var UnknownPlace = Place{
ID: "zz",
LocLabel: "Unknown",
LocCity: "Unknown",
LocState: "Unknown",
LocCountry: "zz",
ID: "zz",
LocLabel: "Unknown",
LocCity: "Unknown",
LocState: "Unknown",
LocCountry: "zz",
LocKeywords: "",
LocNotes: "",
LocFavorite: false,
}
// CreateUnknownPlace initializes default place in the database

View file

@ -32,6 +32,7 @@ type Location struct {
LocState string
LocCountry string
LocSource string
LocKeywords []string
}
type LocationSource interface {
@ -42,9 +43,10 @@ type LocationSource interface {
City() string
State() string
Source() string
Keywords() []string
}
func NewLocation(id string, name string, category string, label string, city string, state string, country string, source string) *Location {
func NewLocation(id, name, category, label, city, state, country, source string, keywords []string) *Location {
result := &Location{
ID: id,
LocName: name,
@ -54,6 +56,7 @@ func NewLocation(id string, name string, category string, label string, city str
LocState: state,
LocCountry: country,
LocSource: source,
LocKeywords: keywords,
}
return result
@ -84,6 +87,7 @@ func (l *Location) QueryPlaces() error {
l.LocCountry = s.CountryCode()
l.LocCategory = s.Category()
l.LocLabel = s.Label()
l.LocKeywords = s.Keywords()
return nil
}
@ -114,6 +118,7 @@ func (l *Location) Assign(s LocationSource) error {
l.LocCountry = s.CountryCode()
l.LocCategory = s.Category()
l.LocLabel = l.label()
l.LocKeywords = s.Keywords()
return nil
}
@ -147,6 +152,10 @@ func (l *Location) label() string {
return strings.Join(loc[:], ", ")
}
func (l Location) S2Token() string {
return l.ID
}
func (l Location) Name() string {
return l.LocName
}
@ -178,3 +187,11 @@ func (l Location) CountryName() string {
func (l Location) Source() string {
return l.LocSource
}
func (l Location) Keywords() []string {
return l.LocKeywords
}
func (l Location) KeywordString() string {
return strings.Join(l.LocKeywords, ", ")
}

View file

@ -15,7 +15,7 @@ func TestLocation_QueryPlaces(t *testing.T) {
lng := 13.40806264572578
id := s2.Token(lat, lng)
l := NewLocation(id, "", "", "", "", "", "", "")
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
if err := l.QueryPlaces(); err != nil {
t.Fatal(err)
@ -27,6 +27,31 @@ func TestLocation_QueryPlaces(t *testing.T) {
}
func TestLocation_Assign(t *testing.T) {
t.Run("Italy", func(t *testing.T) {
id := "47786b2bed37"
o, err := places.FindLocation(id)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "Sotsaslong", o.Name())
assert.Equal(t, "Trentino-Alto Adige/Südtirol", o.State())
assert.Equal(t, "it", o.CountryCode())
var l Location
if err := l.Assign(o); err != nil {
t.Fatal(err)
}
assert.Equal(t, "Sotsaslong", l.LocName)
assert.Equal(t, "Sëlva, Trentino-Alto Adige/Südtirol, Italy", l.LocLabel)
assert.Equal(t, []string{"gardena", "selva", "wolkenstein"}, l.Keywords())
assert.Equal(t, "gardena, selva, wolkenstein", l.KeywordString())
})
t.Run("BerlinFernsehturm", func(t *testing.T) {
lat := 52.5208
lng := 13.40953
@ -50,6 +75,8 @@ func TestLocation_Assign(t *testing.T) {
assert.Equal(t, "Fernsehturm Berlin", l.LocName)
assert.Equal(t, "Berlin, Germany", l.LocLabel)
assert.IsType(t, []string{}, l.Keywords())
assert.Equal(t, "", l.KeywordString())
})
t.Run("SantaMonica", func(t *testing.T) {
@ -179,7 +206,7 @@ func TestLocation_Unknown(t *testing.T) {
lng := 0.0
id := s2.Token(lat, lng)
l := NewLocation(id, "", "", "", "", "", "", "")
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
assert.Equal(t, true, l.Unknown())
})
@ -188,7 +215,7 @@ func TestLocation_Unknown(t *testing.T) {
lng := 29.148046666666666
id := s2.Token(lat, lng)
l := NewLocation(id, "", "", "", "", "", "", "")
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
assert.Equal(t, false, l.Unknown())
})
@ -200,12 +227,12 @@ func TestLocation_place(t *testing.T) {
lng := 0.0
id := s2.Token(lat, lng)
l := NewLocation(id, "", "", "", "", "", "", "")
l := NewLocation(id, "", "", "", "", "", "", "", []string{})
assert.Equal(t, "Unknown", l.label())
})
t.Run("Nürnberg, Bayern, Germany", func(t *testing.T) {
l := NewLocation("", "", "", "", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "", "", "", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "Unknown", l.label())
})
@ -213,7 +240,7 @@ func TestLocation_place(t *testing.T) {
func TestLocation_Name(t *testing.T) {
t.Run("Christkindlesmarkt", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "Christkindlesmarkt", l.Name())
})
@ -221,7 +248,7 @@ func TestLocation_Name(t *testing.T) {
func TestLocation_City(t *testing.T) {
t.Run("Nürnberg", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "Nürnberg", l.City())
})
@ -229,7 +256,7 @@ func TestLocation_City(t *testing.T) {
func TestLocation_State(t *testing.T) {
t.Run("Bayern", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "Bayern", l.State())
})
@ -237,7 +264,7 @@ func TestLocation_State(t *testing.T) {
func TestLocation_Category(t *testing.T) {
t.Run("test", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "test", "", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "test", "", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "test", l.Category())
})
@ -245,7 +272,7 @@ func TestLocation_Category(t *testing.T) {
func TestLocation_Source(t *testing.T) {
t.Run("source", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "source")
l := NewLocation("", "Christkindlesmarkt", "", "", "Nürnberg", "Bayern", "de", "source", []string{})
assert.Equal(t, "source", l.Source())
})
@ -253,7 +280,7 @@ func TestLocation_Source(t *testing.T) {
func TestLocation_Place(t *testing.T) {
t.Run("test-label", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "", "test-label", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "test-label", l.Label())
})
@ -261,7 +288,7 @@ func TestLocation_Place(t *testing.T) {
func TestLocation_CountryCode(t *testing.T) {
t.Run("de", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "de", l.CountryCode())
})
@ -269,14 +296,14 @@ func TestLocation_CountryCode(t *testing.T) {
func TestLocation_CountryName(t *testing.T) {
t.Run("Germany", func(t *testing.T) {
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
l := NewLocation("", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
assert.Equal(t, "Germany", l.CountryName())
})
}
func TestLocation_QueryApi(t *testing.T) {
l := NewLocation("3", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "")
l := NewLocation("3", "Christkindlesmarkt", "test", "test-label", "Nürnberg", "Bayern", "de", "", []string{})
t.Run("xxx", func(t *testing.T) {
api := l.QueryApi("xxx")
assert.Error(t, api, "maps: reverse lookup disabled")

View file

@ -10,7 +10,6 @@ import (
"github.com/melihmucuk/geocache"
"github.com/photoprism/photoprism/pkg/s2"
"github.com/photoprism/photoprism/pkg/txt"
)
type Location struct {
@ -123,7 +122,7 @@ func (l Location) CountryCode() (result string) {
}
func (l Location) Keywords() (result []string) {
return txt.Keywords(l.LocDisplayName)
return result
}
func (l Location) Source() string {

View file

@ -26,7 +26,7 @@ type Location struct {
var ReverseLookupURL = "https://places.photoprism.org/v1/location/%s"
var client = &http.Client{Timeout: 30 * time.Second} // TODO: Change timeout if needed
func NewLocation(id string, lat float64, lng float64, name string, category string, place Place, cached bool) *Location {
func NewLocation(id string, lat, lng float64, name, category string, place Place, cached bool) *Location {
result := &Location{
ID: id,
LocLat: lat,
@ -68,7 +68,15 @@ func FindLocation(id string) (result Location, err error) {
return result, err
}
r, err := client.Do(req)
var r *http.Response
for i := 0; i < 3; i++ {
r, err = client.Do(req)
if err == nil {
break
}
}
if err != nil {
log.Errorf("places: %s", err.Error())
@ -135,7 +143,7 @@ func (l Location) Longitude() (result float64) {
}
func (l Location) Keywords() (result []string) {
return txt.Keywords(l.Label())
return txt.UniqueKeywords(l.Place.LocKeywords)
}
func (l Location) Source() string {

View file

@ -35,7 +35,7 @@ func TestFindLocation(t *testing.T) {
t.Log(l)
})
t.Run("cached true", func(t *testing.T) {
var p = NewPlace("1", "", "", "", "de")
var p = NewPlace("1", "", "", "", "de", "")
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
l, err := FindLocation(location.ID)
if err != nil {
@ -52,7 +52,7 @@ func TestFindLocation(t *testing.T) {
}
func TestLocationGetters(t *testing.T) {
var p = NewPlace("1", "testLabel", "berlin", "berlin", "de")
var p = NewPlace("1", "testLabel", "berlin", "berlin", "de", "foobar")
location := NewLocation("54", 52.51961810676184, 13.40806264572578, "TestLocation", "test", p, true)
t.Run("wrong id", func(t *testing.T) {
assert.Equal(t, "54", location.CellID())
@ -65,7 +65,7 @@ func TestLocationGetters(t *testing.T) {
assert.Equal(t, 52.51961810676184, location.Latitude())
assert.Equal(t, 13.40806264572578, location.Longitude())
assert.Equal(t, "places", location.Source())
assert.Equal(t, []string{"testlabel"}, location.Keywords())
assert.Equal(t, []string{"foobar"}, location.Keywords())
})
}

View file

@ -2,20 +2,22 @@ package places
// Place
type Place struct {
PlaceID string `json:"id"`
LocLabel string `json:"label"`
LocCity string `json:"city"`
LocState string `json:"state"`
LocCountry string `json:"country"`
PlaceID string `json:"id"`
LocLabel string `json:"label"`
LocCity string `json:"city"`
LocState string `json:"state"`
LocCountry string `json:"country"`
LocKeywords string `json:"keywords"`
}
func NewPlace(id string, label string, city string, state string, country string) Place {
func NewPlace(id, label, city, state, country, keywords string) Place {
result := Place{
PlaceID: id,
LocLabel: label,
LocCity: city,
LocState: state,
LocCountry: country,
PlaceID: id,
LocLabel: label,
LocCity: city,
LocState: state,
LocCountry: country,
LocKeywords: keywords,
}
return result