Places: Add support for new keywords field
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
b33983b566
commit
96098913ff
8 changed files with 115 additions and 51 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ", ")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue