From c8a74881d899644569dccfa562cc396f2d3ae9d4 Mon Sep 17 00:00:00 2001
From: Deluan <deluan@deluan.com>
Date: Wed, 22 Jan 2020 01:00:00 -0500
Subject: [PATCH] Fix album lists, to use annotations

---
 engine/list_generator.go             | 55 ++++++++++++++++++++--------
 model/album.go                       |  1 +
 model/annotation.go                  |  1 +
 persistence/album_repository.go      | 13 +++++++
 persistence/annotation_repository.go | 18 +++++++++
 persistence/sql_repository.go        |  6 +--
 6 files changed, 75 insertions(+), 19 deletions(-)

diff --git a/engine/list_generator.go b/engine/list_generator.go
index 1bbddf6c1..d625fca1d 100644
--- a/engine/list_generator.go
+++ b/engine/list_generator.go
@@ -30,10 +30,7 @@ type listGenerator struct {
 	npRepo NowPlayingRepository
 }
 
-// TODO: Only return albums that have the Sort field != empty
-func (g *listGenerator) query(ctx context.Context, qo model.QueryOptions, offset int, size int) (Entries, error) {
-	qo.Offset = offset
-	qo.Max = size
+func (g *listGenerator) query(ctx context.Context, qo model.QueryOptions) (Entries, error) {
 	albums, err := g.ds.Album().GetAll(qo)
 	if err != nil {
 		return nil, err
@@ -49,34 +46,60 @@ func (g *listGenerator) query(ctx context.Context, qo model.QueryOptions, offset
 	return FromAlbums(albums, annMap), err
 }
 
+func (g *listGenerator) queryByAnnotation(ctx context.Context, qo model.QueryOptions) (Entries, error) {
+	annotations, err := g.ds.Annotation().GetAll(getUserID(ctx), model.AlbumItemType, qo)
+	if err != nil {
+		return nil, err
+	}
+	albumIds := make([]string, len(annotations))
+	for i, ann := range annotations {
+		albumIds[i] = ann.ItemID
+	}
+
+	albumMap, err := g.ds.Album().GetMap(albumIds)
+	if err != nil {
+		return nil, err
+	}
+
+	var albums Entries
+	for _, ann := range annotations {
+		album := albumMap[ann.ItemID]
+		albums = append(albums, FromAlbum(&album, &ann))
+	}
+	return albums, nil
+}
+
 func (g *listGenerator) GetNewest(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "CreatedAt", Order: "desc"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "CreatedAt", Order: "desc", Offset: offset, Max: size}
+	return g.query(ctx, qo)
 }
 
 func (g *listGenerator) GetRecent(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "PlayDate", Order: "desc"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "PlayDate", Order: "desc", Offset: offset, Max: size,
+		Filters: map[string]interface{}{"play_date__gt": time.Time{}}}
+	return g.queryByAnnotation(ctx, qo)
 }
 
 func (g *listGenerator) GetFrequent(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "PlayCount", Order: "desc"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "PlayCount", Order: "desc", Offset: offset, Max: size,
+		Filters: map[string]interface{}{"play_count__gt": 0}}
+	return g.queryByAnnotation(ctx, qo)
 }
 
 func (g *listGenerator) GetHighest(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "Rating", Order: "desc"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "Rating", Order: "desc", Offset: offset, Max: size,
+		Filters: map[string]interface{}{"rating__gt": 0}}
+	return g.queryByAnnotation(ctx, qo)
 }
 
 func (g *listGenerator) GetByName(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "Name"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "Name", Offset: offset, Max: size}
+	return g.query(ctx, qo)
 }
 
 func (g *listGenerator) GetByArtist(ctx context.Context, offset int, size int) (Entries, error) {
-	qo := model.QueryOptions{Sort: "Artist"}
-	return g.query(ctx, qo, offset, size)
+	qo := model.QueryOptions{Sort: "Artist", Offset: offset, Max: size}
+	return g.query(ctx, qo)
 }
 
 func (g *listGenerator) GetRandom(ctx context.Context, offset int, size int) (Entries, error) {
diff --git a/model/album.go b/model/album.go
index c8862a6df..de1e0caea 100644
--- a/model/album.go
+++ b/model/album.go
@@ -28,6 +28,7 @@ type AlbumRepository interface {
 	Get(id string) (*Album, error)
 	FindByArtist(artistId string) (Albums, error)
 	GetAll(...QueryOptions) (Albums, error)
+	GetMap(ids []string) (map[string]Album, error)
 	GetRandom(...QueryOptions) (Albums, error)
 	GetStarred(userId string, options ...QueryOptions) (Albums, error)
 	Search(q string, offset int, size int) (Albums, error)
diff --git a/model/annotation.go b/model/annotation.go
index 4c2cd48f7..fef0d37a8 100644
--- a/model/annotation.go
+++ b/model/annotation.go
@@ -24,6 +24,7 @@ type AnnotationMap map[string]Annotation
 
 type AnnotationRepository interface {
 	Get(userID, itemType string, itemID string) (*Annotation, error)
+	GetAll(userID, itemType string, options ...QueryOptions) ([]Annotation, error)
 	GetMap(userID, itemType string, itemID []string) (AnnotationMap, error)
 	Delete(userID, itemType string, itemID ...string) error
 	IncPlayCount(userID, itemType string, itemID string, ts time.Time) error
diff --git a/persistence/album_repository.go b/persistence/album_repository.go
index de45be7b0..712e414fb 100644
--- a/persistence/album_repository.go
+++ b/persistence/album_repository.go
@@ -75,6 +75,19 @@ func (r *albumRepository) GetAll(options ...model.QueryOptions) (model.Albums, e
 	return r.toAlbums(all), nil
 }
 
+func (r *albumRepository) GetMap(ids []string) (map[string]model.Album, error) {
+	var all []album
+	_, err := r.newQuery().Filter("id__in", ids).All(&all)
+	if err != nil {
+		return nil, err
+	}
+	res := make(map[string]model.Album)
+	for _, a := range all {
+		res[a.ID] = model.Album(a)
+	}
+	return res, nil
+}
+
 // TODO Keep order when paginating
 func (r *albumRepository) GetRandom(options ...model.QueryOptions) (model.Albums, error) {
 	sq := r.newRawQuery(options...)
diff --git a/persistence/annotation_repository.go b/persistence/annotation_repository.go
index 8271efda3..7954a1e9a 100644
--- a/persistence/annotation_repository.go
+++ b/persistence/annotation_repository.go
@@ -75,6 +75,24 @@ func (r *annotationRepository) GetMap(userID, itemType string, itemID []string)
 	return m, nil
 }
 
+func (r *annotationRepository) GetAll(userID, itemType string, options ...model.QueryOptions) ([]model.Annotation, error) {
+	if userID == "" {
+		return nil, model.ErrInvalidAuth
+	}
+	q := r.newQuery(options...).Filter("user_id", userID).Filter("item_type", itemType)
+	var res []annotation
+	_, err := q.All(&res)
+	if err != nil {
+		return nil, err
+	}
+
+	all := make([]model.Annotation, len(res))
+	for i, a := range res {
+		all[i] = model.Annotation(a)
+	}
+	return all, err
+}
+
 func (r *annotationRepository) new(userID, itemType string, itemID string) *annotation {
 	id, _ := uuid.NewRandom()
 	return &annotation{
diff --git a/persistence/sql_repository.go b/persistence/sql_repository.go
index c88de2ef1..706596673 100644
--- a/persistence/sql_repository.go
+++ b/persistence/sql_repository.go
@@ -26,6 +26,9 @@ func (r *sqlRepository) newQuery(options ...model.QueryOptions) orm.QuerySeter {
 				q = q.OrderBy(opts.Sort)
 			}
 		}
+		for field, value := range opts.Filters {
+			q = q.Filter(field, value)
+		}
 	}
 	return q
 }
@@ -46,9 +49,6 @@ func (r *sqlRepository) newRawQuery(options ...model.QueryOptions) squirrel.Sele
 				sq = sq.OrderBy(options[0].Sort)
 			}
 		}
-		for field, value := range options[0].Filters {
-			sq = sq.Where(squirrel.Like{field: value.(string) + "%"})
-		}
 	}
 	return sq
 }