From e8e0639d784ac2b760dee0d170bac5e0f44d34a3 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 25 Apr 2020 14:22:47 +0200 Subject: [PATCH] Backend: Provide unknown camera & lens as default Signed-off-by: Michael Mayer --- assets/resources/examples/fixtures.sql | 1 - frontend/src/dialog/photo/details.vue | 3 ++- internal/config/client.go | 6 +++--- internal/config/db.go | 2 ++ internal/entity/camera.go | 13 ++++++++++++- internal/entity/camera_test.go | 16 +++------------- internal/entity/country.go | 10 +++++++--- internal/entity/lens.go | 18 ++++++++++++++---- internal/entity/lens_test.go | 3 ++- internal/entity/photo.go | 3 +++ internal/entity/photo_location.go | 2 +- internal/entity/photo_quality.go | 16 +++++++++++++++- internal/entity/place.go | 23 ++++++++--------------- internal/photoprism/index_mediafile.go | 4 ++-- internal/query/country_test.go | 2 +- 15 files changed, 75 insertions(+), 47 deletions(-) diff --git a/assets/resources/examples/fixtures.sql b/assets/resources/examples/fixtures.sql index 36a4bb937..511e80b56 100644 --- a/assets/resources/examples/fixtures.sql +++ b/assets/resources/examples/fixtures.sql @@ -1,4 +1,3 @@ -INSERT INTO cameras (id, camera_slug, camera_model, camera_make, camera_type, camera_description, camera_notes, created_at, updated_at, deleted_at) VALUES (1, 'unknown', 'Unknown', '', '', '', '', '2020-01-06 02:06:29', '2020-01-06 02:07:26', null); INSERT INTO cameras (id, camera_slug, camera_model, camera_make, camera_type, camera_description, camera_notes, created_at, updated_at, deleted_at) VALUES (2, 'apple-iphone-se', 'iPhone SE', 'Apple', '', '', '', '2020-01-06 02:06:30', '2020-01-06 02:07:28', null); INSERT INTO cameras (id, camera_slug, camera_model, camera_make, camera_type, camera_description, camera_notes, created_at, updated_at, deleted_at) VALUES (3, 'canon-eos-5d', 'EOS 5D', 'Canon', '', '', '', '2020-01-06 02:06:32', '2020-01-06 02:06:32', null); INSERT INTO cameras (id, camera_slug, camera_model, camera_make, camera_type, camera_description, camera_notes, created_at, updated_at, deleted_at) VALUES (4, 'canon-eos-7d', 'EOS 7D', 'Canon', '', '', '', '2020-01-06 02:06:33', '2020-01-06 02:06:33', null); diff --git a/frontend/src/dialog/photo/details.vue b/frontend/src/dialog/photo/details.vue index fad5cca5c..5e193d80b 100644 --- a/frontend/src/dialog/photo/details.vue +++ b/frontend/src/dialog/photo/details.vue @@ -355,7 +355,8 @@ - Apply + Approve + Apply diff --git a/internal/config/client.go b/internal/config/client.go index aea29d347..837e5dc2d 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -167,15 +167,15 @@ func (c *Config) ClientConfig() ClientConfig { db.Model(&entity.Country{}). Select("id, country_name"). - Order("country_name"). + Order("country_slug"). Scan(&countries) db.Where("deleted_at IS NULL"). - Limit(1000).Order("camera_model"). + Limit(10000).Order("camera_slug"). Find(&cameras) db.Where("deleted_at IS NULL"). - Limit(1000).Order("lens_model"). + Limit(10000).Order("lens_slug"). Find(&lenses) db.Where("deleted_at IS NULL AND album_favorite = 1"). diff --git a/internal/config/db.go b/internal/config/db.go index 6f8b0a0b3..58a390410 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -85,6 +85,8 @@ func (c *Config) MigrateDb() { entity.CreateUnknownPlace(db) entity.CreateUnknownCountry(db) + entity.CreateUnknownCamera(db) + entity.CreateUnknownLens(db) } // DropTables drops all tables in the currently configured database (be careful!). diff --git a/internal/entity/camera.go b/internal/entity/camera.go index 08af3bae6..ff13db24e 100644 --- a/internal/entity/camera.go +++ b/internal/entity/camera.go @@ -24,12 +24,23 @@ type Camera struct { DeletedAt *time.Time `sql:"index"` } +var UnknownCamera = Camera{ + CameraModel: "Unknown", + CameraMake: "", + CameraSlug: "zz", +} + +// CreateUnknownCamera initializes the database with an unknown camera if not exists +func CreateUnknownCamera(db *gorm.DB) { + UnknownCamera.FirstOrCreate(db) +} + // NewCamera creates a camera entity from a model name and a make name. func NewCamera(modelName string, makeName string) *Camera { makeName = strings.TrimSpace(makeName) if modelName == "" { - modelName = "Unknown" + return &UnknownCamera } else if strings.HasPrefix(modelName, makeName) { modelName = strings.TrimSpace(modelName[len(makeName):]) } diff --git a/internal/entity/camera_test.go b/internal/entity/camera_test.go index 6323a4be2..14cfbd034 100644 --- a/internal/entity/camera_test.go +++ b/internal/entity/camera_test.go @@ -7,15 +7,10 @@ import ( ) func TestNewCamera(t *testing.T) { - t.Run("model Unknown make Nikon", func(t *testing.T) { + t.Run("unknown camera", func(t *testing.T) { camera := NewCamera("", "Nikon") - expected := &Camera{ - CameraModel: "Unknown", - CameraMake: "Nikon", - CameraSlug: "nikon-unknown", - } - assert.Equal(t, expected, camera) + assert.Equal(t, &UnknownCamera, camera) }) t.Run("model EOS 6D make Canon", func(t *testing.T) { camera := NewCamera("EOS 6D", "Canon") @@ -50,12 +45,7 @@ func TestNewCamera(t *testing.T) { t.Run("model Unknown make Unknown", func(t *testing.T) { camera := NewCamera("", "") - expected := &Camera{ - CameraModel: "Unknown", - CameraMake: "", - CameraSlug: "unknown", - } - assert.Equal(t, expected, camera) + assert.Equal(t, &UnknownCamera, camera) }) } diff --git a/internal/entity/country.go b/internal/entity/country.go index 221d8175a..48823d4fd 100644 --- a/internal/entity/country.go +++ b/internal/entity/country.go @@ -26,8 +26,12 @@ type Country struct { New bool `gorm:"-"` } -// UnknownCountry is the default country -var UnknownCountry = NewCountry("zz", maps.CountryNames["zz"]) +// UnknownCountry is defined here to use it as a default +var UnknownCountry = Country{ + ID: "zz", + CountryName: maps.CountryNames["zz"], + CountrySlug: "zz", +} // CreateUnknownCountry is used to initialize the database with the default country func CreateUnknownCountry(db *gorm.DB) { @@ -37,7 +41,7 @@ func CreateUnknownCountry(db *gorm.DB) { // NewCountry creates a new country, with default country code if not provided func NewCountry(countryCode string, countryName string) *Country { if countryCode == "" { - countryCode = "zz" + return &UnknownCountry } if altName, ok := altCountryNames[countryName]; ok { diff --git a/internal/entity/lens.go b/internal/entity/lens.go index 0562cd5bd..8558a1ea8 100644 --- a/internal/entity/lens.go +++ b/internal/entity/lens.go @@ -24,6 +24,17 @@ type Lens struct { DeletedAt *time.Time `sql:"index"` } +var UnknownLens = Lens{ + LensModel: "Unknown", + LensMake: "", + LensSlug: "zz", +} + +// CreateUnknownLens initializes the database with an unknown lens if not exists +func CreateUnknownLens(db *gorm.DB) { + UnknownLens.FirstOrCreate(db) +} + // TableName returns Lens table identifier "lens" func (Lens) TableName() string { return "lenses" @@ -33,13 +44,12 @@ func (Lens) TableName() string { func NewLens(modelName string, makeName string) *Lens { modelName = strings.TrimSpace(modelName) makeName = strings.TrimSpace(makeName) + lensSlug := slug.MakeLang(modelName, "en") if modelName == "" { - modelName = "Unknown" + return &UnknownLens } - lensSlug := slug.MakeLang(modelName, "en") - result := &Lens{ LensModel: modelName, LensMake: makeName, @@ -49,7 +59,7 @@ func NewLens(modelName string, makeName string) *Lens { return result } -// FirstOrCreate checks wether the lens already exists in the database +// FirstOrCreate checks if the lens already exists in the database func (m *Lens) FirstOrCreate(db *gorm.DB) *Lens { mutex.Db.Lock() defer mutex.Db.Unlock() diff --git a/internal/entity/lens_test.go b/internal/entity/lens_test.go index 3fa5b4078..e512386eb 100644 --- a/internal/entity/lens_test.go +++ b/internal/entity/lens_test.go @@ -17,7 +17,8 @@ func TestNewLens(t *testing.T) { lens := NewLens("", "") assert.Equal(t, "Unknown", lens.LensModel) assert.Equal(t, "", lens.LensMake) - assert.Equal(t, "unknown", lens.LensSlug) + assert.Equal(t, "zz", lens.LensSlug) + assert.Equal(t, &UnknownLens, lens) }) } diff --git a/internal/entity/photo.go b/internal/entity/photo.go index ef204de26..6401aa2cb 100644 --- a/internal/entity/photo.go +++ b/internal/entity/photo.go @@ -62,6 +62,7 @@ type Photo struct { Labels []PhotoLabel CreatedAt time.Time UpdatedAt time.Time + EditedAt *time.Time DeletedAt *time.Time `sql:"index"` } @@ -100,6 +101,8 @@ func SavePhotoForm(model Photo, form form.Photo, db *gorm.DB, geoApi string) err log.Warnf("%s (%s)", err.Error(), model.PhotoUUID) } + edited := time.Now().UTC() + model.EditedAt = &edited model.PhotoQuality = model.QualityScore() return db.Unscoped().Save(&model).Error diff --git a/internal/entity/photo_location.go b/internal/entity/photo_location.go index e58eb65a0..4f03225c4 100644 --- a/internal/entity/photo_location.go +++ b/internal/entity/photo_location.go @@ -84,7 +84,7 @@ func (m *Photo) UpdateLocation(db *gorm.DB, geoApi string) (keywords []string, l } else { log.Warn(err) - m.Place = UnknownPlace + m.Place = &UnknownPlace m.PlaceID = UnknownPlace.ID } diff --git a/internal/entity/photo_quality.go b/internal/entity/photo_quality.go index 72e30d4cf..935905f6e 100644 --- a/internal/entity/photo_quality.go +++ b/internal/entity/photo_quality.go @@ -2,6 +2,7 @@ package entity import ( "strings" + "time" "github.com/photoprism/photoprism/pkg/txt" ) @@ -12,6 +13,11 @@ var QualityBlacklist = map[string]bool{ "info": true, } +var ( + year2008 = time.Date(2008, 1,1,0,0,0,0, time.UTC) + year2012 = time.Date(2012, 1,1,0,0,0,0, time.UTC) +) + // QualityScore returns a score based on photo properties like size and metadata. func (m *Photo) QualityScore() (score int) { if m.PhotoFavorite { @@ -26,7 +32,11 @@ func (m *Photo) QualityScore() (score int) { score++ } - if m.PhotoResolution >= 2 { + if m.TakenAt.Before(year2008) { + score++ + } else if m.TakenAt.Before(year2012) && m.PhotoResolution >= 1 { + score++ + } else if m.PhotoResolution >= 2 { score++ } @@ -49,5 +59,9 @@ func (m *Photo) QualityScore() (score int) { score++ } + if score < 3 && m.EditedAt != nil { + score = 3 + } + return score } diff --git a/internal/entity/place.go b/internal/entity/place.go index a6b3edb82..ec80b43d0 100644 --- a/internal/entity/place.go +++ b/internal/entity/place.go @@ -22,8 +22,14 @@ type Place struct { New bool `gorm:"-"` } -// UnknownPlace is the default unknown place -var UnknownPlace = NewPlace("-", "Unknown", "Unknown", "Unknown", "zz") +// UnknownPlace is defined here to use it as a default +var UnknownPlace = Place{ + ID: "-", + LocLabel: "Unknown", + LocCity: "Unknown", + LocState: "Unknown", + LocCountry: "zz", +} // CreateUnknownPlace initializes default place in the database func CreateUnknownPlace(db *gorm.DB) { @@ -57,19 +63,6 @@ func FindPlaceByLabel(id string, label string, db *gorm.DB) *Place { return place } -// NewPlace registers a new place in database -func NewPlace(id, label, city, state, countryCode string) *Place { - result := &Place{ - ID: id, - LocLabel: label, - LocCity: city, - LocState: state, - LocCountry: countryCode, - } - - return result -} - // Find returns db record of place func (m *Place) Find(db *gorm.DB) error { if err := db.First(m, "id = ?", m.ID).Error; err != nil { diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index a63c02c5d..b3d816708 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -232,7 +232,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( } else { log.Info("index: no latitude and longitude in metadata") - photo.Place = entity.UnknownPlace + photo.Place = &entity.UnknownPlace photo.PlaceID = entity.UnknownPlace.ID } } @@ -263,7 +263,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) ( } if photo.Place == nil { - photo.Place = entity.UnknownPlace + photo.Place = &entity.UnknownPlace photo.PlaceID = entity.UnknownPlace.ID } diff --git a/internal/query/country_test.go b/internal/query/country_test.go index 1ea35e973..90b92b8d6 100644 --- a/internal/query/country_test.go +++ b/internal/query/country_test.go @@ -20,7 +20,7 @@ func TestNewCountry(t *testing.T) { country := entity.NewCountry("", "") assert.Equal(t, "zz", country.ID) assert.Equal(t, "Unknown", country.CountryName) - assert.Equal(t, "unknown", country.CountrySlug) + assert.Equal(t, "zz", country.CountrySlug) }) } func TestCountry_FirstOrCreate(t *testing.T) {