Backend: Fix handling of deleted labels

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-06-02 17:57:12 +02:00
parent c2adaa5752
commit 15113afaa6
5 changed files with 60 additions and 10 deletions

View file

@ -215,7 +215,17 @@ func BatchLabelsDelete(router *gin.RouterGroup, conf *config.Config) {
log.Infof("labels: deleting %#v", f.Labels)
entity.Db().Where("label_uid IN (?)", f.Labels).Delete(&entity.Label{})
var labels entity.Labels
if err := entity.Db().Where("label_uid IN (?)", f.Labels).Find(&labels).Error; err != nil {
logError("labels", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrDeleteFailed)
return
}
for _, label := range labels {
logError("labels", label.Delete())
}
UpdateClientConfig(conf)

View file

@ -20,6 +20,7 @@ var (
ErrFileNotFound = gin.H{"code": http.StatusNotFound, "error": "File not found"}
ErrUnexpectedError = gin.H{"code": http.StatusInternalServerError, "error": "Unexpected error"}
ErrSaveFailed = gin.H{"code": http.StatusInternalServerError, "error": "Changes could not be saved"}
ErrDeleteFailed = gin.H{"code": http.StatusInternalServerError, "error": "Changes could not be saved"}
ErrFormInvalid = gin.H{"code": http.StatusBadRequest, "error": "Changes could not be saved"}
ErrFeatureDisabled = gin.H{"code": http.StatusForbidden, "error": "Feature disabled"}
)

View file

@ -46,6 +46,10 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
if err := labelEntity.Restore(); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "could not restore label"})
}
photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(m.ID, labelEntity.ID, f.Uncertainty, "manual"))
if photoLabel == nil {

View file

@ -11,6 +11,8 @@ import (
"github.com/photoprism/photoprism/pkg/txt"
)
type Labels []Label
// Label is used for photo, album and location categorization
type Label struct {
ID uint `gorm:"primary_key" json:"ID" yaml:"-"`
@ -40,7 +42,7 @@ func (m *Label) BeforeCreate(scope *gorm.Scope) error {
return scope.SetColumn("LabelUID", rnd.PPID('l'))
}
// NewLabel creates a label in database with a given name and priority
// NewLabel returns a new label.
func NewLabel(name string, priority int) *Label {
labelName := txt.Clip(name, txt.ClipDefault)
@ -62,26 +64,47 @@ func NewLabel(name string, priority int) *Label {
return result
}
// Save updates the existing or inserts a new row.
// Save updates the existing or inserts a new label.
func (m *Label) Save() error {
return Db().Save(m).Error
}
// Create inserts a new row to the database.
// Create inserts the label to the database.
func (m *Label) Create() error {
return Db().Create(m).Error
}
// Updates a column in the database.
// Delete removes the label from the database.
func (m *Label) Delete() error {
Db().Where("label_id = ? OR category_id = ?", m.ID, m.ID).Delete(&Category{})
Db().Where("label_id = ?", m.ID).Delete(&PhotoLabel{})
return Db().Delete(m).Error
}
// Deleted returns true if the label is deleted.
func (m *Label) Deleted() bool {
return m.DeletedAt != nil
}
// Delete removes the label from the database.
func (m *Label) Restore() error {
if m.Deleted() {
return UnscopedDb().Model(m).Update("DeletedAt", nil).Error
}
return nil
}
// Updates a label property in the database.
func (m *Label) Update(attr string, value interface{}) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
}
// FirstOrCreateLabel returns the existing row, inserts a new row or nil in case of errors.
// FirstOrCreateLabel returns the existing label, inserts a new label or nil in case of errors.
func FirstOrCreateLabel(m *Label) *Label {
result := Label{}
if err := Db().Where("label_slug = ? OR custom_slug = ?", m.LabelSlug, m.CustomSlug).First(&result).Error; err == nil {
if err := UnscopedDb().Where("label_slug = ? OR custom_slug = ?", m.LabelSlug, m.CustomSlug).First(&result).Error; err == nil {
return &result
} else if err := m.Create(); err != nil {
log.Errorf("label: %s", err)
@ -167,6 +190,10 @@ func (m *Label) UpdateClassify(label classify.Label) error {
continue
}
if sn.Deleted() {
continue
}
if err := db.Model(m).Association("LabelCategories").Append(sn).Error; err != nil {
return err
}

View file

@ -17,7 +17,6 @@ import (
"github.com/ulule/deepcopier"
)
// Default photo result slice for simple use cases.
type Photos []Photo
// UIDs returns a slice of unique photo IDs.
@ -273,6 +272,10 @@ func (m *Photo) SyncKeywordLabels() error {
for _, w := range keywords {
if label := FindLabel(w); label != nil {
if label.Deleted() {
continue
}
labelIds = append(labelIds, label.ID)
FirstOrCreatePhotoLabel(NewPhotoLabel(m.ID, label.ID, 25, classify.SrcKeyword))
}
@ -585,7 +588,12 @@ func (m *Photo) AddLabels(labels classify.Labels) {
labelEntity := FirstOrCreateLabel(NewLabel(classifyLabel.Title(), classifyLabel.Priority))
if labelEntity == nil {
log.Errorf("index: label %s for photo %d should not be nil - bug?", txt.Quote(classifyLabel.Title()), m.ID)
log.Errorf("index: label %s should not be nil - bug? (%s)", txt.Quote(classifyLabel.Title()), m)
continue
}
if labelEntity.Deleted() {
log.Debugf("index: skipping deleted label %s (%s)", txt.Quote(classifyLabel.Title()), m)
continue
}
@ -596,7 +604,7 @@ func (m *Photo) AddLabels(labels classify.Labels) {
photoLabel := FirstOrCreatePhotoLabel(NewPhotoLabel(m.ID, labelEntity.ID, classifyLabel.Uncertainty, classifyLabel.Source))
if photoLabel == nil {
log.Errorf("index: label %d for photo %d should not be nil - bug?", labelEntity.ID, m.ID)
log.Errorf("index: photo-label %d should not be nil - bug? (%s)", labelEntity.ID, m)
continue
}