From 894608c19e62b1f1db30a2ac5609d8c4647a52a6 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Tue, 7 May 2019 11:39:25 +0200 Subject: [PATCH 1/7] Fix Stage.teams returning nil on error --- app/models/stage.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/stage.rb b/app/models/stage.rb index e1aa980..9246352 100644 --- a/app/models/stage.rb +++ b/app/models/stage.rb @@ -8,8 +8,12 @@ class Stage < ApplicationRecord delegate :owner, to: :tournament def teams - return matches.map(&:teams).flatten.uniq unless matches.size.zero? - - groups.map(&:teams).flatten.uniq unless groups.size.zero? + if !matches.size.zero? + matches.map(&:teams).flatten.uniq + elsif !groups.size.zero? + groups.map(&:teams).flatten.uniq + else + [] + end end end From bdcfbe934b0891b675d72ce0c54bef9afcd2edc9 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Wed, 8 May 2019 14:59:29 +0200 Subject: [PATCH 2/7] Add StatisticsService --- app/services/statistics_service.rb | 25 +++++++++++ spec/services/statistics_service_spec.rb | 54 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 app/services/statistics_service.rb create mode 100644 spec/services/statistics_service_spec.rb diff --git a/app/services/statistics_service.rb b/app/services/statistics_service.rb new file mode 100644 index 0000000..72c2b09 --- /dev/null +++ b/app/services/statistics_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class StatisticsService + def initialize(stage) + raise 'Unsupported stage type' if stage.nil? || stage.groups.empty? + + @stage = stage + @group_scores = sort_group_scores(@stage.groups, :group_points) + + @most_dominant_score = sort_group_scores(@stage.groups, :scored_points).first + @least_dominant_score = sort_group_scores(@stage.groups, :received_points).first + end + + attr_reader :group_scores + attr_reader :most_dominant_score + attr_reader :least_dominant_score + + private + + def sort_group_scores(groups, by) + groups + .map(&:group_scores).flatten # collect all group scores + .sort_by(&by).reverse + end +end diff --git a/spec/services/statistics_service_spec.rb b/spec/services/statistics_service_spec.rb new file mode 100644 index 0000000..6e53708 --- /dev/null +++ b/spec/services/statistics_service_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +RSpec.describe StatisticsService do + before do + # build tournament with predictable test data + tournament = create(:tournament) + group_stage = create(:group_stage) + group = group_stage.groups.first + @most_dominant_score = GroupScore.new team: create(:team), + group_points: 100, + scored_points: 100, received_points: 0 + @least_dominant_score = GroupScore.new team: create(:team), + group_points: 0, + scored_points: 0, received_points: 100 + group.group_scores << @most_dominant_score + group.group_scores << @least_dominant_score + tournament.stages << group_stage + @service = StatisticsService.new group_stage + end + + describe '#new' do + context 'with a playoff stage' do + it 'throws an exception' do + expect do + StatisticsService.new create(:playoff_stage) + end.to raise_error(RuntimeError) + end + end + + context 'with a group stage' do + it 'succeeds' do + StatisticsService.new create(:group_stage) + end + end + end + + describe '#most_dominant_score' do + it 'returns the most dominant group score' do + expect(@service.most_dominant_score.id).to eq(@most_dominant_score.id) + end + end + + describe '#least_dominant_score' do + it 'returns the least dominant group score' do + expect(@service.least_dominant_score.id).to eq(@least_dominant_score.id) + end + end + + describe '#group_scores' do + it 'returns an array containing all group scores' do + expect(@service.group_scores.length).to eq(GroupScore.count) + end + end +end From 99bfe6ed704457813afb690fae5e6a776d4096d4 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Mon, 13 May 2019 14:19:15 +0200 Subject: [PATCH 3/7] Add StatisticsController --- app/controllers/statistics_controller.rb | 26 ++++++++++ .../controllers/statistics_controller_spec.rb | 50 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 app/controllers/statistics_controller.rb create mode 100644 spec/controllers/statistics_controller_spec.rb diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb new file mode 100644 index 0000000..0b25abf --- /dev/null +++ b/app/controllers/statistics_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class StatisticsController < ApplicationController + before_action :set_tournament, only: %i[index] + + # GET /tournaments/1/statistics + def index + group_stage = @tournament.stages.find_by(level: -1) + if group_stage + service = StatisticsService.new group_stage + render json: { + most_dominant_score: service.most_dominant_score, + least_dominant_score: service.least_dominant_score, + group_scores: service.group_scores + } + else + render json: {}, status: :not_implemented + end + end + + private + + def set_tournament + @tournament = Tournament.find(params[:tournament_id]) + end +end diff --git a/spec/controllers/statistics_controller_spec.rb b/spec/controllers/statistics_controller_spec.rb new file mode 100644 index 0000000..ff43d90 --- /dev/null +++ b/spec/controllers/statistics_controller_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe StatisticsController, type: :controller do + describe 'GET #index' do + context 'tournament without a group stage' do + it 'returns a not implemented response' do + get :index, params: { tournament_id: create(:tournament).to_param } + expect(response).to have_http_status(:not_implemented) + end + end + + context 'tournament with a group stage' do + before do + @tournament = create(:group_stage_tournament) + @group_stage = @tournament.stages.find_by(level: -1) + @most_dominant_score = GroupScore.new team: @tournament.teams.first, + group_points: 100, + scored_points: 100, received_points: 0 + @least_dominant_score = GroupScore.new team: @tournament.teams.first, + group_points: 0, + scored_points: 0, received_points: 100 + @tournament.stages.first.groups.first.group_scores << @most_dominant_score + @tournament.stages.first.groups.first.group_scores << @least_dominant_score + @tournament.save! + end + + it 'returns a success response' do + get :index, params: { tournament_id: @tournament.to_param } + expect(response).to be_successful + end + + it 'returns a list containing all group scores' do + get :index, params: { tournament_id: @tournament.to_param } + expect(deserialize_response(response)[:group_scores].length).to eq(GroupScore.count) + end + + it 'returns a most dominant group score' do + get :index, params: { tournament_id: @tournament.to_param } + expect(deserialize_response(response)[:most_dominant_score][:id]).to eq(@most_dominant_score.id) + end + + it 'returns a least dominant group score' do + get :index, params: { tournament_id: @tournament.to_param } + expect(deserialize_response(response)[:least_dominant_score][:id]).to eq(@least_dominant_score.id) + end + end + end +end From b307abfc43b3fefb5040aab70b35c6cfd3136008 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Mon, 13 May 2019 14:19:28 +0200 Subject: [PATCH 4/7] Add routing to StatisticsController --- config/routes.rb | 4 +++- spec/routing/statistics_routing_spec.rb | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 spec/routing/statistics_routing_spec.rb diff --git a/config/routes.rb b/config/routes.rb index 16b79b6..10cafbf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,8 @@ Rails.application.routes.draw do resources :matches, only: %i[show update] resources :teams, only: %i[show update] - resources :tournaments + resources :tournaments do + resources :statistics, only: %i[index] + end resources :match_scores, only: %i[show update] end diff --git a/spec/routing/statistics_routing_spec.rb b/spec/routing/statistics_routing_spec.rb new file mode 100644 index 0000000..f2f6f11 --- /dev/null +++ b/spec/routing/statistics_routing_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe StatisticsController, type: :routing do + describe 'routing' do + it 'routes to #index' do + expect(get: '/tournaments/1/statistics').to route_to('statistics#index', tournament_id: '1') + end + end +end From d6a691fe5a9edfa4f9c0e56f31d9e0513ef9a839 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Tue, 14 May 2019 21:43:46 +0200 Subject: [PATCH 5/7] Add documentation to sort_group_scores --- app/services/statistics_service.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/services/statistics_service.rb b/app/services/statistics_service.rb index 72c2b09..62ae754 100644 --- a/app/services/statistics_service.rb +++ b/app/services/statistics_service.rb @@ -17,9 +17,15 @@ class StatisticsService private - def sort_group_scores(groups, by) + # Sort group scores associated with `groups` by GroupScore#`attr` + # in descending order + # + # @param groups [Array] Groups to take GroupScore objects from + # @param attr [Symbol] GroupScore attribute to sort by + # @return [Array] Sorted array of group scores + def sort_group_scores(groups, attr) groups .map(&:group_scores).flatten # collect all group scores - .sort_by(&by).reverse + .sort_by(&attr).reverse end end From 37b63cace29a1f9b778086046fd12e185d523762 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Tue, 14 May 2019 22:26:57 +0200 Subject: [PATCH 6/7] Use methods instead of attributes because each attribute is only used once and updating a Stage should reflect those changes --- app/services/statistics_service.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/services/statistics_service.rb b/app/services/statistics_service.rb index 62ae754..22f302f 100644 --- a/app/services/statistics_service.rb +++ b/app/services/statistics_service.rb @@ -5,15 +5,19 @@ class StatisticsService raise 'Unsupported stage type' if stage.nil? || stage.groups.empty? @stage = stage - @group_scores = sort_group_scores(@stage.groups, :group_points) - - @most_dominant_score = sort_group_scores(@stage.groups, :scored_points).first - @least_dominant_score = sort_group_scores(@stage.groups, :received_points).first end - attr_reader :group_scores - attr_reader :most_dominant_score - attr_reader :least_dominant_score + def group_scores + sort_group_scores(@stage.groups, :group_points) + end + + def most_dominant_score + sort_group_scores(@stage.groups, :scored_points).first + end + + def least_dominant_score + sort_group_scores(@stage.groups, :received_points).first + end private From 39fc9f1630f88132843a91c506e77df069050d48 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Wed, 15 May 2019 11:59:28 +0200 Subject: [PATCH 7/7] Use factories instead of creating models manually --- spec/controllers/statistics_controller_spec.rb | 14 ++++++++------ spec/services/statistics_service_spec.rb | 12 ++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/spec/controllers/statistics_controller_spec.rb b/spec/controllers/statistics_controller_spec.rb index ff43d90..36673b2 100644 --- a/spec/controllers/statistics_controller_spec.rb +++ b/spec/controllers/statistics_controller_spec.rb @@ -15,12 +15,14 @@ RSpec.describe StatisticsController, type: :controller do before do @tournament = create(:group_stage_tournament) @group_stage = @tournament.stages.find_by(level: -1) - @most_dominant_score = GroupScore.new team: @tournament.teams.first, - group_points: 100, - scored_points: 100, received_points: 0 - @least_dominant_score = GroupScore.new team: @tournament.teams.first, - group_points: 0, - scored_points: 0, received_points: 100 + @most_dominant_score = create(:group_score, + team: @tournament.teams.first, + group_points: 100, + scored_points: 100, received_points: 0) + @least_dominant_score = create(:group_score, + team: @tournament.teams.first, + group_points: 0, + scored_points: 0, received_points: 100) @tournament.stages.first.groups.first.group_scores << @most_dominant_score @tournament.stages.first.groups.first.group_scores << @least_dominant_score @tournament.save! diff --git a/spec/services/statistics_service_spec.rb b/spec/services/statistics_service_spec.rb index 6e53708..ec6b3fa 100644 --- a/spec/services/statistics_service_spec.rb +++ b/spec/services/statistics_service_spec.rb @@ -6,12 +6,12 @@ RSpec.describe StatisticsService do tournament = create(:tournament) group_stage = create(:group_stage) group = group_stage.groups.first - @most_dominant_score = GroupScore.new team: create(:team), - group_points: 100, - scored_points: 100, received_points: 0 - @least_dominant_score = GroupScore.new team: create(:team), - group_points: 0, - scored_points: 0, received_points: 100 + @most_dominant_score = create(:group_score, + group_points: 100, + scored_points: 100, received_points: 0) + @least_dominant_score = create(:group_score, + group_points: 0, + scored_points: 0, received_points: 100) group.group_scores << @most_dominant_score group.group_scores << @least_dominant_score tournament.stages << group_stage