Skip to content

Commit

Permalink
Add endpoint to get play stats (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
robbevp authored Jun 30, 2022
1 parent 6cc96dc commit 0735a9a
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 4 deletions.
2 changes: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ApplicationController < ActionController::API
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :model_not_found

has_scope :sorted, default: nil, allow_blank: true
has_scope :sorted, default: nil, allow_blank: true, except: :stats

serialization_scope :url_options

Expand Down
17 changes: 16 additions & 1 deletion app/controllers/plays_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
class PlaysController < ApplicationController
has_scope :by_album, as: 'album_id'
has_scope :by_artist, as: 'artist_id'
has_scope :played_before, as: 'played_before'
has_scope :played_after, as: 'played_after'

def index
authorize Play
@plays = apply_scopes(policy_scope(Play))
.order(id: :desc)
.paginate(page: params[:page], per_page: params[:per_page])
add_pagination_headers(@plays)

Expand All @@ -22,6 +24,19 @@ def create
end
end

def stats
authorize Play
@stats = apply_scopes(policy_scope(Play))
.left_joins(track: :audio_file)
.select('COUNT(*)', 'MAX(played_at) as last_played_at', 'SUM(COALESCE("audio_files"."length", 0)) as total_length', :track_id, :user_id)
.group(:track_id, :user_id)
.order(track_id: :desc)
.paginate(page: params[:page], per_page: params[:per_page])
add_pagination_headers(@stats)

render json: @stats, each_serializer: PlayStatSerializer
end

private

def transformed_attributes
Expand Down
5 changes: 4 additions & 1 deletion app/models/play.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ class Play < ApplicationRecord

validates :played_at, presence: true

scope :by_album, ->(album) { joins(:track).where(track: { album_id: album }) }
scope :by_album, ->(album) { where(track_id: Track.by_album(album)) }
scope :by_artist, ->(artist) { where(track_id: Track.by_artist(artist)) }
scope :played_before, ->(date) { where('played_at < ?', date) }
scope :played_after, ->(date) { where('played_at > ?', date) }
end
4 changes: 4 additions & 0 deletions app/policies/play_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def create?
user.present?
end

def stats?
user.present?
end

def permitted_attributes
%i[track_id played_at]
end
Expand Down
3 changes: 3 additions & 0 deletions app/serializers/play_stat_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class PlayStatSerializer < ActiveModel::Serializer
attributes :count, :track_id, :last_played_at, :total_length
end
7 changes: 6 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
# PATCH /api/playlists/:id(.:format) playlists#update
# PUT /api/playlists/:id(.:format) playlists#update
# DELETE /api/playlists/:id(.:format) playlists#destroy
# stats_plays GET /api/plays/stats(.:format) plays#stats
# plays GET /api/plays(.:format) plays#index
# POST /api/plays(.:format) plays#create
# destroy_empty_tracks POST /api/tracks/destroy_empty(.:format) tracks#destroy_empty
Expand Down Expand Up @@ -143,7 +144,11 @@
end
resources :locations, only: %i[index show create destroy]
resources :playlists
resources :plays, only: %i[index create]
resources :plays, only: %i[index create] do
collection do
get 'stats'
end
end
resources :tracks do
collection do
post 'destroy_empty'
Expand Down
47 changes: 47 additions & 0 deletions test/controllers/plays_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,51 @@ class PlaysControllerTest < ActionDispatch::IntegrationTest

assert_response 422
end

test 'should get stats and not return play stats for other users' do
create(:play, track: @track, user: create(:user))
get stats_plays_url
assert_response :success
body = JSON.parse response.body
assert_empty body
end

test 'should get stats and return play stats for users' do
create(:play, track: @track, user: @user, played_at: DateTime.new(2022, 0o1, 0o2, 0o3, 0o4, 0o5))
get stats_plays_url
assert_response :success
body = JSON.parse response.body
assert_equal 1, body[0]['count']
assert_equal '2022-01-02T03:04:05.000Z', body[0]['last_played_at']
end

test 'should get stats and filter plays by album' do
create(:play, track: @track, user: @user)
create(:play, track: create(:track), user: @user)
get stats_plays_url(album_id: @track.album_id)
assert_response :success
body = JSON.parse response.body
assert_equal 1, body.length
end

test 'should get stats and filter plays by date' do
create(:play, track: @track, user: @user, played_at: 5.days.ago)
create(:play, track: @track, user: @user, played_at: 3.days.ago)
create(:play, track: @track, user: @user, played_at: DateTime.current)
create(:play, track: @track, user: @user)
get stats_plays_url(played_before: 2.days.ago, played_after: 4.days.ago)
assert_response :success
body = JSON.parse response.body
assert_equal 1, body[0]['count']
end

test 'should get stats and filter plays by artist' do
ta = create(:track_artist, track: @track)
create(:play, track: @track, user: @user)
create(:play, track: create(:track_artist).track, user: @user)
get stats_plays_url(artist_id: ta.artist_id)
assert_response :success
body = JSON.parse response.body
assert_equal 1, body.length
end
end

0 comments on commit 0735a9a

Please sign in to comment.