mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-18 07:30:29 +00:00
feat(ratings): enhance user rating stats retrieval with user-specific filtering and improve error messages
This commit is contained in:
parent
85a285ac7c
commit
b564acfa18
@ -81,6 +81,6 @@ type UserRepository interface {
|
||||
GetUserLibraries(userID string) (Libraries, error)
|
||||
SetUserLibraries(userID string, libraryIDs []int) error
|
||||
|
||||
RatingStats(ctx context.Context) ([]UserRatingStats, error)
|
||||
RatingStats(ctx context.Context, userID string) ([]UserRatingStats, error)
|
||||
RatingItems(ctx context.Context, userID, itemType string, rating int) ([]RatedItem, error)
|
||||
}
|
||||
|
||||
@ -478,7 +478,7 @@ func (r *userRepository) SetUserLibraries(userID string, libraryIDs []int) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepository) RatingStats(ctx context.Context) ([]model.UserRatingStats, error) {
|
||||
func (r *userRepository) RatingStats(ctx context.Context, userID string) ([]model.UserRatingStats, error) {
|
||||
type row struct {
|
||||
UserID string `db:"user_id"`
|
||||
UserName string `db:"user_name"`
|
||||
@ -487,10 +487,14 @@ func (r *userRepository) RatingStats(ctx context.Context) ([]model.UserRatingSta
|
||||
Count int `db:"cnt"`
|
||||
}
|
||||
|
||||
where := And{Gt{"a.rating": 0}, Eq{"a.item_type": []interface{}{"media_file", "album"}}}
|
||||
if userID != "" {
|
||||
where = append(where, Eq{"a.user_id": userID})
|
||||
}
|
||||
sel := Select(`a.user_id`, `u.user_name`, `a.item_type`, `a.rating`, `count(*) as cnt`).
|
||||
From(`annotation a`).
|
||||
Join(`"user" u ON a.user_id = u.id`).
|
||||
Where(And{Gt{"a.rating": 0}, Eq{"a.item_type": []interface{}{"media_file", "album"}}}).
|
||||
Where(where).
|
||||
GroupBy(`a.user_id`, `u.user_name`, `a.item_type`, `a.rating`).
|
||||
OrderBy(`u.user_name`, `a.item_type`, `a.rating DESC`)
|
||||
|
||||
|
||||
@ -19,22 +19,16 @@ func (api *Router) addRatingStatsRoute(r chi.Router) {
|
||||
func getRatingStats(ds model.DataStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser, _ := request.UserFrom(r.Context())
|
||||
stats, err := ds.User(r.Context()).RatingStats(r.Context())
|
||||
filterUserID := ""
|
||||
if !currentUser.IsAdmin {
|
||||
filterUserID = currentUser.ID
|
||||
}
|
||||
stats, err := ds.User(r.Context()).RatingStats(r.Context(), filterUserID)
|
||||
if err != nil {
|
||||
log.Error(r.Context(), "Error getting rating stats", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if !currentUser.IsAdmin {
|
||||
filtered := make([]model.UserRatingStats, 0, 1)
|
||||
for _, s := range stats {
|
||||
if s.UserID == currentUser.ID {
|
||||
filtered = append(filtered, s)
|
||||
break
|
||||
}
|
||||
}
|
||||
stats = filtered
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(stats); err != nil {
|
||||
log.Error(r.Context(), "Error encoding rating stats", err)
|
||||
@ -50,13 +44,21 @@ func getRatingItems(ds model.DataStore) http.HandlerFunc {
|
||||
ratingStr := r.URL.Query().Get("rating")
|
||||
|
||||
rating, err := strconv.Atoi(ratingStr)
|
||||
if err != nil || rating < 1 || rating > 5 || userID == "" || (itemType != "album" && itemType != "song") {
|
||||
http.Error(w, "invalid parameters", http.StatusBadRequest)
|
||||
if err != nil || rating < 1 || rating > 5 {
|
||||
http.Error(w, "rating must be between 1 and 5", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if userID == "" {
|
||||
http.Error(w, "userId is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if itemType != "album" && itemType != "song" {
|
||||
http.Error(w, "type must be 'album' or 'song'", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !currentUser.IsAdmin && currentUser.ID != userID {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
http.Error(w, "non-admin users can only query their own ratings", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -137,7 +137,7 @@ func (u *MockedUserRepo) SetUserLibraries(userID string, libraryIDs []int) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MockedUserRepo) RatingStats(ctx context.Context) ([]model.UserRatingStats, error) {
|
||||
func (u *MockedUserRepo) RatingStats(ctx context.Context, userID string) ([]model.UserRatingStats, error) {
|
||||
if u.Error != nil {
|
||||
return nil, u.Error
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export const AverageRatingField = ({ className, size, record = {}, ...rest }) =>
|
||||
const classes = useStyles()
|
||||
const translate = useTranslate()
|
||||
|
||||
const avg = record.averageRating || 0
|
||||
const avg = Number(record.averageRating) || 0
|
||||
if (avg <= 0) return null
|
||||
|
||||
return (
|
||||
|
||||
@ -62,10 +62,8 @@ const UserRatingItems = ({ match }) => {
|
||||
const title = `${userName} · ${typeLabel} · ${rating}★`
|
||||
|
||||
const getCoverUrl = (item) => {
|
||||
return subsonic.getCoverArtUrl(
|
||||
{ id: item.id, albumArtist: type === 'album' ? item.artist : undefined, album: type === 'song' ? item.name : undefined, updatedAt: item.updatedAt },
|
||||
40,
|
||||
)
|
||||
const coverArtId = type === 'album' ? item.id : item.albumId
|
||||
return subsonic.getCoverArtUrl({ id: coverArtId, updatedAt: item.updatedAt }, 40)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -131,7 +131,12 @@ const RatingTable = ({ stats, label, userId, userName, type }) => {
|
||||
onClick={() => count > 0 && history.push(linkTo)}
|
||||
tabIndex={count > 0 ? 0 : undefined}
|
||||
role={count > 0 ? 'button' : undefined}
|
||||
onKeyDown={count > 0 ? (e) => (e.key === 'Enter' || e.key === ' ') && history.push(linkTo) : undefined}
|
||||
onKeyDown={count > 0 ? (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') {
|
||||
e.preventDefault()
|
||||
history.push(linkTo)
|
||||
}
|
||||
} : undefined}
|
||||
>
|
||||
<td className={classes.ratingCell}>{r}.0</td>
|
||||
<td className={classes.countCell}>{count}</td>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user