From ce379f7a3022c1252beecfbf28d541314c72fc5e Mon Sep 17 00:00:00 2001 From: Bogdanov Anton Date: Thu, 29 Feb 2024 18:22:43 +0300 Subject: [PATCH] added updating oraculs lineups points for cups --- .../admin/cups/rounds_controller.rb | 16 ++++++ app/jobs/games/import_job.rb | 3 +- app/jobs/oraculs/lineups/points/update_job.rb | 7 ++- app/models/cups/round.rb | 2 + app/models/week.rb | 2 + .../oraculs/lineups/points/update_service.rb | 40 ++++++++------- .../admin/cups/rounds/index.html.erb | 1 + config/locales/en.yml | 2 + config/locales/ru.yml | 2 + config/routes.rb | 4 ++ .../admin/cups/rounds_controller_spec.rb | 38 ++++++++++++++ .../oraculs/lineups/points/update_job_spec.rb | 46 +++++++++++++---- .../lineups/points/update_service_spec.rb | 51 ++++++++++++++----- 13 files changed, 169 insertions(+), 45 deletions(-) diff --git a/app/controllers/admin/cups/rounds_controller.rb b/app/controllers/admin/cups/rounds_controller.rb index 48a85642..a621cc26 100644 --- a/app/controllers/admin/cups/rounds_controller.rb +++ b/app/controllers/admin/cups/rounds_controller.rb @@ -7,6 +7,7 @@ class RoundsController < Admin::BaseController before_action :find_cups_rounds, only: %i[index] before_action :find_cup, only: %i[new create] + before_action :find_cups_round, only: %i[refresh_oraculs_points] def index; end @@ -24,6 +25,17 @@ def create end end + def refresh_oraculs_points + ::Oraculs::Lineups::Points::UpdateJob.perform_later( + periodable_id: @cups_round.id, + periodable_type: 'Cups::Round' + ) + redirect_to( + admin_cup_rounds_path(cup_id: @cups_round.cup_id), + notice: t('controllers.admin.cups.rounds.refresh_oraculs_points') + ) + end + private def find_cups_rounds @@ -38,6 +50,10 @@ def find_cup @cup = Cup.find(params[:cup_id]) end + def find_cups_round + @cups_round = ::Cups::Round.find(params[:cups_round_id]) + end + def cup_params params.require(:cups_round).permit(:name, :position) end diff --git a/app/jobs/games/import_job.rb b/app/jobs/games/import_job.rb index e982ae18..2034b258 100644 --- a/app/jobs/games/import_job.rb +++ b/app/jobs/games/import_job.rb @@ -22,7 +22,8 @@ def perform(game_ids:, main_external_source:) ) ::Oraculs::Lineups::Points::UpdateJob.perform_later( - week_id: week.id + periodable_id: week.id, + periodable_type: 'Week' ) end end diff --git a/app/jobs/oraculs/lineups/points/update_job.rb b/app/jobs/oraculs/lineups/points/update_job.rb index 8553e881..7c0d2d48 100644 --- a/app/jobs/oraculs/lineups/points/update_job.rb +++ b/app/jobs/oraculs/lineups/points/update_job.rb @@ -6,10 +6,9 @@ module Points class UpdateJob < ApplicationJob queue_as :default - def perform(week_id:) - update_points.call(week_id: week_id) - - Week.find_by(id: week_id)&.season&.oracul_leagues&.each do |oracul_league| + def perform(periodable_id:, periodable_type:) + update_points.call(periodable_id: periodable_id, periodable_type: periodable_type) + periodable_type.constantize.find_by(id: periodable_id)&.placeable&.oracul_leagues&.each do |oracul_league| update_current_place.call(oracul_league: oracul_league) end end diff --git a/app/models/cups/round.rb b/app/models/cups/round.rb index 8a9d319e..59784509 100644 --- a/app/models/cups/round.rb +++ b/app/models/cups/round.rb @@ -18,5 +18,7 @@ class Round < ApplicationRecord scope :future, -> { where(status: [ACTIVE, COMING]) } enum status: { COMING => 0, ACTIVE => 1, FINISHED => 2 } + + alias placeable cup end end diff --git a/app/models/week.rb b/app/models/week.rb index 7fb183f0..85772944 100644 --- a/app/models/week.rb +++ b/app/models/week.rb @@ -35,6 +35,8 @@ class Week < ApplicationRecord delegate :league, to: :season + alias placeable season + def previous Week.find_by(season_id: season_id, position: position - 1) end diff --git a/app/services/oraculs/lineups/points/update_service.rb b/app/services/oraculs/lineups/points/update_service.rb index f5650a88..52b22ab3 100644 --- a/app/services/oraculs/lineups/points/update_service.rb +++ b/app/services/oraculs/lineups/points/update_service.rb @@ -7,16 +7,16 @@ class UpdateService include Deps[oraculs_update_points: 'services.oraculs.points.update'] # rubocop: disable Metrics/AbcSize - def call(week_id:) + def call(periodable_id:, periodable_type:) oracul_ids = [] - games = games(week_id) - grouped_forecasts = forecasts(games.pluck(:id)) + forecastables = forecastables(periodable_id, periodable_type) + grouped_forecasts = forecasts(forecastables.pluck(:id), periodable_type) - lineups = oraculs_lineups(week_id).map do |lineup| + lineups = oraculs_lineups(periodable_id, periodable_type).map do |lineup| oracul_ids << lineup[:oracul_id] lineup.merge( - points: games.each_with_index.inject(0) { |acc, (game, index)| - acc + find_points(game, grouped_forecasts[lineup[:id]][index]) + points: forecastables.each_with_index.inject(0) { |acc, (forecastable, index)| + acc + find_points(forecastable, grouped_forecasts[lineup[:id]][index]) } ) end @@ -29,41 +29,47 @@ def call(week_id:) private - def oraculs_lineups(week_id) + def oraculs_lineups(periodable_id, periodable_type) Oraculs::Lineup - .where(periodable_id: week_id, periodable_type: 'Week') + .where(periodable_id: periodable_id, periodable_type: periodable_type) .hashable_pluck(:id, :uuid, :oracul_id, :periodable_id, :periodable_type) end - def games(week_id) - Game - .where(week_id: week_id) + def forecastables(periodable_id, periodable_type) + games_relation(periodable_id, periodable_type) .where.not(points: []) .order(id: :asc) .hashable_pluck(:id, :points) end - def forecasts(game_ids) + def games_relation(periodable_id, periodable_type) + return Game.where(week_id: periodable_id) if periodable_type == 'Week' + + Cups::Pair.where(cups_round_id: periodable_id) + end + + def forecasts(forecastable_ids, periodable_type) + forecastable_type = periodable_type == 'Week' ? 'Game' : 'Cups::Pair' Oraculs::Forecast - .where(forecastable_id: game_ids, forecastable_type: 'Game') + .where(forecastable_id: forecastable_ids, forecastable_type: forecastable_type) .order(forecastable_id: :asc) .hashable_pluck(:oraculs_lineup_id, :forecastable_id, :value) .group_by { |element| element[:oraculs_lineup_id] } end # rubocop: disable Metrics/AbcSize - def find_points(game, forecast) + def find_points(forecastable, forecast) return 0 if forecast[:value].empty? - game_home_score = game[:points][0] + game_home_score = forecastable[:points][0] forecast_home_score = forecast[:value][0] - game_difference = game[:points][0] - game[:points][1] + game_difference = forecastable[:points][0] - forecastable[:points][1] forecast_difference = forecast[:value][0] - forecast[:value][1] return 3 if game_home_score == forecast_home_score && game_difference == forecast_difference return 2 if game_difference == forecast_difference - return 1 if difference_result(game[:points]) == difference_result(forecast[:value]) + return 1 if difference_result(forecastable[:points]) == difference_result(forecast[:value]) 0 end diff --git a/app/views/controllers/admin/cups/rounds/index.html.erb b/app/views/controllers/admin/cups/rounds/index.html.erb index b4ee0f2a..c74c13c3 100644 --- a/app/views/controllers/admin/cups/rounds/index.html.erb +++ b/app/views/controllers/admin/cups/rounds/index.html.erb @@ -19,6 +19,7 @@ <%= cups_round[:position] %> <%= link_to t('views.admin.cups.rounds.index.pairs'), admin_cups_round_pairs_path(cups_round_id: cups_round[:id]), class: 'btn-primary btn-small' %> + <%= link_to t('views.admin.cups.rounds.index.refresh_oraculs_points'), admin_refresh_oraculs_points_path(cups_round_id: cups_round[:id]), class: 'btn-primary btn-small' %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index ab0805f5..d9bad7e8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -65,6 +65,7 @@ en: rounds: create: success: Cups round is created + refresh_oraculs_points: Refreshing is started pairs: create: success: Cups pair is created @@ -195,6 +196,7 @@ en: name: Name position: Position pairs: Games + refresh_oraculs_points: Refresh oraculs new: create: Create new cups round pairs: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 0c6c038f..b142bfcd 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -65,6 +65,7 @@ ru: rounds: create: success: Кубковый раунд создан + refresh_oraculs_points: Обновление оракулов начато pairs: create: success: Кубковая игры создана @@ -195,6 +196,7 @@ ru: name: Название position: Положение pairs: Игры + refresh_oraculs_points: Обновить оракулов new: create: Создать новый раунд pairs: diff --git a/config/routes.rb b/config/routes.rb index 41ccbd3f..d5b87c02 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,10 @@ resources :pairs, only: %i[index new edit create update] end end + scope module: :cups do + get 'cups_rounds/:cups_round_id/refresh_oraculs_points', to: 'rounds#refresh_oraculs_points', + as: :refresh_oraculs_points + end resources :weeks, only: %i[index edit update] end diff --git a/spec/controllers/admin/cups/rounds_controller_spec.rb b/spec/controllers/admin/cups/rounds_controller_spec.rb index f0b7c1b6..5f5e1341 100644 --- a/spec/controllers/admin/cups/rounds_controller_spec.rb +++ b/spec/controllers/admin/cups/rounds_controller_spec.rb @@ -98,4 +98,42 @@ def do_request post :create, params: { cup_id: 'unexisting', cups_round: { name: '1/8', position: '1' } } end end + + describe 'GET#refresh_oraculs_points' do + it_behaves_like 'required auth' + it_behaves_like 'required email confirmation' + it_behaves_like 'required available email' + it_behaves_like 'required admin' + + context 'for admin' do + sign_in_admin + + before { allow(Oraculs::Lineups::Points::UpdateJob).to receive(:perform_later) } + + context 'for unexisting cups round' do + it 'render not found page' do + do_request + + expect(response).to render_template 'shared/404' + end + end + + context 'for existing cups round' do + let!(:cups_round) { create :cups_round, cup: cup } + + it 'runs background job', :aggregate_failures do + get :refresh_oraculs_points, params: { cups_round_id: cups_round.id, locale: 'en' } + + expect(Oraculs::Lineups::Points::UpdateJob).to( + have_received(:perform_later).with(periodable_id: cups_round.id, periodable_type: 'Cups::Round') + ) + expect(response).to redirect_to admin_cup_rounds_path(cup_id: cup.id) + end + end + end + + def do_request + get :refresh_oraculs_points, params: { cups_round_id: 'unexisting', locale: 'en' } + end + end end diff --git a/spec/jobs/oraculs/lineups/points/update_job_spec.rb b/spec/jobs/oraculs/lineups/points/update_job_spec.rb index 8b074809..fe19922b 100644 --- a/spec/jobs/oraculs/lineups/points/update_job_spec.rb +++ b/spec/jobs/oraculs/lineups/points/update_job_spec.rb @@ -1,9 +1,12 @@ # frozen_string_literal: true describe Oraculs::Lineups::Points::UpdateJob, type: :service do - subject(:job_call) { described_class.perform_now(week_id: week.id) } + subject(:job_call) { + described_class.perform_now(periodable_id: periodable.id, periodable_type: periodable.class.name) + } - let!(:season) { create :season } + let!(:league) { create :league } + let!(:season) { create :season, league: league } let!(:week) { create :week, season: season } let!(:oracul_place) { create :oracul_place, placeable: season } let!(:oracul_league) { create :oracul_league, oracul_place: oracul_place } @@ -13,14 +16,37 @@ allow(FantasySports::Container.resolve('services.oracul_leagues.members.update_current_place')).to receive(:call) end - it 'calls services', :aggregate_failures do - job_call + context 'for week' do + let(:periodable) { week } - expect(FantasySports::Container.resolve('services.oraculs.lineups.points.update')).to( - have_received(:call).with(week_id: week.id) - ) - expect(FantasySports::Container.resolve('services.oracul_leagues.members.update_current_place')).to( - have_received(:call).with(oracul_league: oracul_league) - ) + it 'calls services', :aggregate_failures do + job_call + + expect(FantasySports::Container.resolve('services.oraculs.lineups.points.update')).to( + have_received(:call).with(periodable_id: week.id, periodable_type: 'Week') + ) + expect(FantasySports::Container.resolve('services.oracul_leagues.members.update_current_place')).to( + have_received(:call).with(oracul_league: oracul_league) + ) + end + end + + context 'for cups round' do + let!(:cup) { create :cup, league: league } + let!(:cups_round) { create :cups_round, cup: cup } + let(:periodable) { cups_round } + + before { oracul_place.update!(placeable: cup) } + + it 'calls services', :aggregate_failures do + job_call + + expect(FantasySports::Container.resolve('services.oraculs.lineups.points.update')).to( + have_received(:call).with(periodable_id: cups_round.id, periodable_type: 'Cups::Round') + ) + expect(FantasySports::Container.resolve('services.oracul_leagues.members.update_current_place')).to( + have_received(:call).with(oracul_league: oracul_league) + ) + end end end diff --git a/spec/services/oraculs/lineups/points/update_service_spec.rb b/spec/services/oraculs/lineups/points/update_service_spec.rb index 46f2d38d..a35fb2b8 100644 --- a/spec/services/oraculs/lineups/points/update_service_spec.rb +++ b/spec/services/oraculs/lineups/points/update_service_spec.rb @@ -1,24 +1,49 @@ # frozen_string_literal: true describe Oraculs::Lineups::Points::UpdateService, type: :service do - subject(:service_call) { described_class.new.call(week_id: week.id) } + subject(:service_call) { + described_class.new.call(periodable_id: periodable.id, periodable_type: periodable.class.name) + } - let!(:week) { create :week } - let!(:oraculs_lineup) { create :oraculs_lineup, periodable: week } + context 'for week' do + let!(:periodable) { create :week } + let!(:oraculs_lineup) { create :oraculs_lineup, periodable: periodable } - before do - game1 = create :game, week: week, points: [1, 0] - game2 = create :game, week: week, points: [1, 0] - game3 = create :game, week: week, points: [1, 0] + before do + game1 = create :game, week: periodable, points: [1, 0] + game2 = create :game, week: periodable, points: [1, 0] + game3 = create :game, week: periodable, points: [1, 0] - create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game1, value: [1, 0] - create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game2, value: [2, 0] - create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game3, value: [0, 3] + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game1, value: [1, 0] + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game2, value: [2, 0] + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: game3, value: [0, 3] + end + + it 'updates points' do + service_call + + expect(oraculs_lineup.reload.points).to eq 4 + end end - it 'updates points' do - service_call + context 'for cups round' do + let!(:periodable) { create :cups_round } + let!(:oraculs_lineup) { create :oraculs_lineup, periodable: periodable } + + before do + cups_pair1 = create :cups_pair, cups_round: periodable, points: [1, 0] + cups_pair2 = create :cups_pair, cups_round: periodable, points: [1, 0] + cups_pair3 = create :cups_pair, cups_round: periodable, points: [1, 0] + + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: cups_pair1, value: [1, 0] + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: cups_pair2, value: [2, 0] + create :oraculs_forecast, oraculs_lineup: oraculs_lineup, forecastable: cups_pair3, value: [0, 3] + end + + it 'updates points' do + service_call - expect(oraculs_lineup.reload.points).to eq 4 + expect(oraculs_lineup.reload.points).to eq 4 + end end end