mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -779,23 +779,38 @@ func (m *Photo) ShouldGenerateLabels(force bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AddLabels ensures classify labels exist as Label entities and attaches them to the photo, tightening uncertainty when higher confidence values arrive.
|
||||
// AddLabels ensures classify labels exist as Label entities and attaches them to the photo.
|
||||
// Labels are skipped when they have no usable title or carry 0% probability so that UpdateClassify
|
||||
// never receives invalid input from upstream detectors.
|
||||
func (m *Photo) AddLabels(labels classify.Labels) {
|
||||
for _, classifyLabel := range labels {
|
||||
labelEntity := FirstOrCreateLabel(NewLabel(classifyLabel.Title(), classifyLabel.Priority))
|
||||
|
||||
title := classifyLabel.Title()
|
||||
|
||||
if title == "" || txt.Slug(title) == "" {
|
||||
log.Debugf("index: skipping blank label (%s)", m)
|
||||
continue
|
||||
}
|
||||
|
||||
if classifyLabel.Uncertainty >= 100 {
|
||||
log.Debugf("index: skipping label %s with zero probability (%s)", title, m)
|
||||
continue
|
||||
}
|
||||
|
||||
labelEntity := FirstOrCreateLabel(NewLabel(title, classifyLabel.Priority))
|
||||
|
||||
if labelEntity == nil {
|
||||
log.Errorf("index: label %s could not be created (%s)", clean.Log(classifyLabel.Title()), m)
|
||||
log.Errorf("index: label %s could not be created (%s)", clean.Log(title), m)
|
||||
continue
|
||||
}
|
||||
|
||||
if labelEntity.Deleted() {
|
||||
log.Debugf("index: skipping deleted label %s (%s)", clean.Log(classifyLabel.Title()), m)
|
||||
log.Debugf("index: skipping deleted label %s (%s)", clean.Log(title), m)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := labelEntity.UpdateClassify(classifyLabel); err != nil {
|
||||
log.Errorf("index: failed to update label %s (%s)", clean.Log(classifyLabel.Title()), err)
|
||||
log.Errorf("index: failed to update label %s (%s)", clean.Log(title), err)
|
||||
}
|
||||
|
||||
labelSrc := classifyLabel.Source
|
||||
|
||||
@@ -439,6 +439,15 @@ func TestPhoto_GetDetails(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPhoto_AddLabels(t *testing.T) {
|
||||
resetLabel := func(t *testing.T, photoName, labelName, src string, uncertainty int) {
|
||||
t.Helper()
|
||||
photo := PhotoFixtures.Get(photoName)
|
||||
label := LabelFixtures.Get(labelName)
|
||||
assert.NoError(t, UnscopedDb().Model(&PhotoLabel{}).
|
||||
Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).
|
||||
UpdateColumns(Values{"Uncertainty": uncertainty, "LabelSrc": src}).Error)
|
||||
}
|
||||
|
||||
t.Run("Add", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
||||
classifyLabels := classify.Labels{{Name: "cactus", Uncertainty: 30, Source: SrcManual, Priority: 5, Categories: []string{"plant"}}}
|
||||
@@ -457,15 +466,6 @@ func TestPhoto_AddLabels(t *testing.T) {
|
||||
assert.Equal(t, 10, m.Labels[0].Uncertainty)
|
||||
assert.Equal(t, SrcManual, m.Labels[0].LabelSrc)
|
||||
})
|
||||
resetLabel := func(t *testing.T, photoName, labelName, src string, uncertainty int) {
|
||||
t.Helper()
|
||||
photo := PhotoFixtures.Get(photoName)
|
||||
label := LabelFixtures.Get(labelName)
|
||||
assert.NoError(t, UnscopedDb().Model(&PhotoLabel{}).
|
||||
Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).
|
||||
UpdateColumns(Values{"Uncertainty": uncertainty, "LabelSrc": src}).Error)
|
||||
}
|
||||
|
||||
t.Run("OllamaReplacesLowerConfidence", func(t *testing.T) {
|
||||
photoName := "Photo15"
|
||||
labelName := "landscape"
|
||||
@@ -514,6 +514,47 @@ func TestPhoto_AddLabels(t *testing.T) {
|
||||
assert.Equal(t, 15, updated.Uncertainty)
|
||||
assert.Equal(t, SrcOllama, updated.LabelSrc)
|
||||
})
|
||||
t.Run("SkipBlankTitle", func(t *testing.T) {
|
||||
photo := PhotoFixtures.Get("Photo15")
|
||||
initialLen := len(photo.Labels)
|
||||
|
||||
var labelCountBefore int
|
||||
if err := Db().Model(&Label{}).Where("label_slug = ?", "unknown").Count(&labelCountBefore).Error; err != nil {
|
||||
t.Fatalf("count before failed: %v", err)
|
||||
}
|
||||
|
||||
classifyLabels := classify.Labels{{Name: " ", Uncertainty: 30, Source: SrcManual}}
|
||||
photo.AddLabels(classifyLabels)
|
||||
|
||||
assert.Equal(t, initialLen, len(photo.Labels))
|
||||
|
||||
var labelCountAfter int
|
||||
if err := Db().Model(&Label{}).Where("label_slug = ?", "unknown").Count(&labelCountAfter).Error; err != nil {
|
||||
t.Fatalf("count after failed: %v", err)
|
||||
}
|
||||
assert.Equal(t, labelCountBefore, labelCountAfter)
|
||||
})
|
||||
t.Run("SkipZeroProbability", func(t *testing.T) {
|
||||
photo := PhotoFixtures.Get("Photo15")
|
||||
initialLen := len(photo.Labels)
|
||||
|
||||
labelSlug := "zero-probability"
|
||||
var labelCountBefore int
|
||||
if err := Db().Model(&Label{}).Where("label_slug = ?", labelSlug).Count(&labelCountBefore).Error; err != nil {
|
||||
t.Fatalf("count before failed: %v", err)
|
||||
}
|
||||
|
||||
classifyLabels := classify.Labels{{Name: "Zero Probability", Uncertainty: 100, Source: SrcManual}}
|
||||
photo.AddLabels(classifyLabels)
|
||||
|
||||
assert.Equal(t, initialLen, len(photo.Labels))
|
||||
|
||||
var labelCountAfter int
|
||||
if err := Db().Model(&Label{}).Where("label_slug = ?", labelSlug).Count(&labelCountAfter).Error; err != nil {
|
||||
t.Fatalf("count after failed: %v", err)
|
||||
}
|
||||
assert.Equal(t, labelCountBefore, labelCountAfter)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPhoto_Delete(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user