From ce0d022e45e20997bfc354967328ffd17f399519 Mon Sep 17 00:00:00 2001 From: Motoya Nakamura Date: Wed, 16 Jul 2025 17:22:40 +0900 Subject: [PATCH 01/33] add api routes --- serverside_challenge_2/challenge/config/routes.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index 262ffd547..939c05c63 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/config/routes.rb @@ -3,4 +3,9 @@ # Defines the root path route ("/") # root "articles#index" + namespace :api do + namespace :v1 do + post "simulate_all_plan", to: "plan#simulate_all" + end + end end From f0863abaea7ec0c57910f74507f3acc26ce6741b Mon Sep 17 00:00:00 2001 From: Motoya Nakamura Date: Wed, 16 Jul 2025 17:23:11 +0900 Subject: [PATCH 02/33] add api controller with test code --- .../challenge/app/controllers/api/v1/plan_controller.rb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb new file mode 100644 index 000000000..307f5f603 --- /dev/null +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb @@ -0,0 +1,8 @@ +class Api::V1::PlanController < ApplicationController + def simulate_all + amps = params[:amps] + meter_rate = params[:meter_rate] + data = { privider_name: "test", plan_name: "test", price: "test", amps: amps, meter_rate: meter_rate} + render json: data + end +end From 54088760aa9e0156afc1e76b7a82fa6462b7ac63 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 21 Jul 2025 20:11:55 +0900 Subject: [PATCH 03/33] add gem active_hash --- serverside_challenge_2/challenge/Gemfile | 2 ++ serverside_challenge_2/challenge/Gemfile.lock | 3 +++ 2 files changed, 5 insertions(+) diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index 43bf67fe3..bc3972329 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -36,6 +36,8 @@ gem "bootsnap", require: false # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem "rack-cors" +gem "active_hash" + group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri mingw x64_mingw ] diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index a47fb85f5..20720c885 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -46,6 +46,8 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_hash (3.3.1) + activesupport (>= 5.0.0) activejob (7.0.8) activesupport (= 7.0.8) globalid (>= 0.3.6) @@ -168,6 +170,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + active_hash bootsnap debug pg (~> 1.1) From 51405de10a344c3c93455c60146a4f02d0c3cc6c Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 21 Jul 2025 20:14:34 +0900 Subject: [PATCH 04/33] add csv files --- .../challenge/config/data/basic_charges.csv | 26 +++++++++++++++++++ .../config/data/meter_rate_charges.csv | 11 ++++++++ .../config/data/power_supply_plans.csv | 5 ++++ 3 files changed, 42 insertions(+) create mode 100644 serverside_challenge_2/challenge/config/data/basic_charges.csv create mode 100644 serverside_challenge_2/challenge/config/data/meter_rate_charges.csv create mode 100644 serverside_challenge_2/challenge/config/data/power_supply_plans.csv diff --git a/serverside_challenge_2/challenge/config/data/basic_charges.csv b/serverside_challenge_2/challenge/config/data/basic_charges.csv new file mode 100644 index 000000000..542c906b6 --- /dev/null +++ b/serverside_challenge_2/challenge/config/data/basic_charges.csv @@ -0,0 +1,26 @@ +id,plan_id,amp,price +1,1,10,286.00 +2,1,15,429.00 +3,1,20,572.00 +4,1,30,858.00 +5,1,40,1144.00 +6,1,50,1430.00 +7,1,60,1716.00 +8,2,10,311.75 +9,2,15,467.63 +10,2,20,623.50 +11,2,30,935.25 +12,2,40,1247.00 +13,2,50,1558.75 +14,2,60,1870.50 +15,3,30,858.00 +16,3,40,1144.00 +17,3,50,1430.00 +18,3,60,1716.00 +19,4,10,0.00 +20,4,15,0.00 +21,4,20,0.00 +22,4,30,0.00 +23,4,40,0.00 +24,4,50,0.00 +25,4,60,0.00 \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv b/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv new file mode 100644 index 000000000..2aa4fffae --- /dev/null +++ b/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv @@ -0,0 +1,11 @@ +id,plan_id,min_meter_rate,max_meter_rate,price +1,1,0,120,19.88 +2,1,121,300,26.48 +3,1,301,,30.57 +4,2,0,120,29.80 +5,2,121,300,36.40 +6,2,301,,40.49 +7,3,0,140,23.67 +8,3,141,350,23.88 +9,3,351,,26.41 +10,4,0,,28.8 \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/power_supply_plans.csv b/serverside_challenge_2/challenge/config/data/power_supply_plans.csv new file mode 100644 index 000000000..042a8bc32 --- /dev/null +++ b/serverside_challenge_2/challenge/config/data/power_supply_plans.csv @@ -0,0 +1,5 @@ +id,provider_name,plan_name +1,東京電力エナジーパートナー,従量電灯B +2,東京電力エナジーパートナー,スタンダードS +3,東京ガス,ずっとも電気1 +4,Looopでんき,おうちプラン \ No newline at end of file From eaadf5ae5d4c49eed3d9bbef8dde345dbf609ba8 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 21 Jul 2025 20:15:17 +0900 Subject: [PATCH 05/33] add models --- .../challenge/app/models/basic_charge.rb | 21 +++++++++++++++++ .../challenge/app/models/meter_rate_charge.rb | 23 +++++++++++++++++++ .../challenge/app/models/power_supply_plan.rb | 6 +++++ 3 files changed, 50 insertions(+) create mode 100644 serverside_challenge_2/challenge/app/models/basic_charge.rb create mode 100644 serverside_challenge_2/challenge/app/models/meter_rate_charge.rb create mode 100644 serverside_challenge_2/challenge/app/models/power_supply_plan.rb diff --git a/serverside_challenge_2/challenge/app/models/basic_charge.rb b/serverside_challenge_2/challenge/app/models/basic_charge.rb new file mode 100644 index 000000000..07d1a1fdc --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/basic_charge.rb @@ -0,0 +1,21 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class BasicCharge < ActiveHash::Base + include ActiveHash::Associations + parsed_data = [] + data = CSV.read('config/data/basic_charges.csv', headers: true).map(&:to_hash) + data.each do |row| + id = row["id"].to_i + plan_id = row["plan_id"].to_i + amp = row["amp"].to_i + price = row["price"].to_f + puts row.to_h + parsed_data << { + id: id, + plan_id: plan_id, + amp: amp, + price: price + } + end + self.data = parsed_data +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb new file mode 100644 index 000000000..7625b3cfc --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb @@ -0,0 +1,23 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class MeterRateCharge < ActiveHash::Base + include ActiveHash::Associations + parsed_data = [] + data = CSV.read('config/data/meter_rate_charges.csv', headers: true).map(&:to_hash) + data.each do |row| + id = row["id"].to_i + plan_id = row["plan_id"].to_i + min_meter_rate = row["min_meter_rate"].to_i + max_meter_rate = row["max_meter_rate"].nil? ? nil : row["max_meter_rate"].to_i + price = row["price"].to_f + puts row.to_h + parsed_data << { + id: id, + plan_id: plan_id, + min_meter_rate: min_meter_rate, + max_meter_rate: max_meter_rate, + price: price + } + end + self.data = parsed_data +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb new file mode 100644 index 000000000..bbfaf301f --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb @@ -0,0 +1,6 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class PowerSupplyPlan < ActiveHash::Base + include ActiveHash::Associations + self.data = CSV.read('config/data/power_supply_plans.csv', headers: true).map(&:to_hash) +end \ No newline at end of file From 4970ab22631725ca037d4cc8ff2f771031646fb1 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 21 Jul 2025 20:19:52 +0900 Subject: [PATCH 06/33] rename controller, add codes --- .../app/controllers/api/v1/plan_controller.rb | 8 -- .../api/v1/power_supply_plan_controller.rb | 109 ++++++++++++++++++ .../challenge/config/routes.rb | 2 +- 3 files changed, 110 insertions(+), 9 deletions(-) delete mode 100644 serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb create mode 100644 serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb deleted file mode 100644 index 307f5f603..000000000 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/plan_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -class Api::V1::PlanController < ApplicationController - def simulate_all - amps = params[:amps] - meter_rate = params[:meter_rate] - data = { privider_name: "test", plan_name: "test", price: "test", amps: amps, meter_rate: meter_rate} - render json: data - end -end diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb new file mode 100644 index 000000000..19eacb4d0 --- /dev/null +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -0,0 +1,109 @@ +class Api::V1::PowerSupplyPlanController < ApplicationController + def simulate_all + amp = params[:amp] + meter_rate = params[:meter_rate] + validate_result = validate_request(amp, meter_rate) + return render json: validate_result if validate_result.present? + + amp = amp.to_i + meter_rate = meter_rate.to_i + + # 各プランの基本料金を取得 + plans = PowerSupplyPlan.all + result = [] + for plan in plans do + id = plan.id + basic_charge_price = basic_charge(id, amp) + if basic_charge_price == -1 + next + end + meter_charge_price = meter_charge(id, meter_rate) + if meter_charge_price == -1 + next + end + + result << { + provider_name: plan.provider_name.to_s, + plan_name: plan.plan_name.to_s, + price: (basic_charge_price + meter_charge_price).round + } + end + + render json: result + end + + private + + def basic_charge(plan_id, amp) + basic_charge_record = BasicCharge.where(plan_id: plan_id, amp: amp) + if basic_charge_record.count == 1 + price = basic_charge_record.first.price + elsif basic_charge_record.count == 0 + return -1 + end + + return price + end + + def meter_charge(plan_id, meter_rate) + plan = MeterRateCharge.where( + plan_id: plan_id, + min_meter_rate: ..meter_rate, + max_meter_rate: meter_rate.. + ) + logger.debug "plan.bank?: #{plan.blank?}" + + if plan.blank? + maximum_plan = MeterRateCharge.where( + plan_id: plan_id, + max_meter_rate: nil + ) + if maximum_plan.first.min_meter_rate <= meter_rate + return maximum_plan.first.price * meter_rate.to_f + else + return -1 + end + else + + return plan.first.price * meter_rate.to_f + end + end + + def validate_request(amp, meter_rate) + message = "" + if amp.blank? + message = message + "リクエストパラメータ 'amp' が不足しています。\n" + else + message = message + "リクエストパラメータ 'amp' の値が不正です。0以上の整数を指定してください。\n" unless numeric?(amp) && amp.to_i >= 0 + end + if meter_rate.blank? + message = message + "リクエストパラメータ 'meter_rate' が不足しています。\n" + else + message = message + "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。\n" unless numeric?(meter_rate) && meter_rate.to_i >= 0 + end + + if message.present? + return errro_result(message) + end + + return [] + end + + def numeric?(string) + Integer(string) + true + rescue ArgumentError, TypeError + false + end + + def errro_result(result) + [ + { + error: { + code: 400, + message: result + } + } + ] + end +end diff --git a/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index 939c05c63..001968f1d 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/config/routes.rb @@ -5,7 +5,7 @@ # root "articles#index" namespace :api do namespace :v1 do - post "simulate_all_plan", to: "plan#simulate_all" + post "simulate_all_plan", to: "power_supply_plan#simulate_all" end end end From 74a4b2f7fc4a2a29adbdf36a0bbf35da73b201fe Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Tue, 22 Jul 2025 01:14:17 +0900 Subject: [PATCH 07/33] add secret_key_base for production --- serverside_challenge_2/challenge/config/credentials.yml.enc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/config/credentials.yml.enc b/serverside_challenge_2/challenge/config/credentials.yml.enc index 57c5aee24..690711894 100644 --- a/serverside_challenge_2/challenge/config/credentials.yml.enc +++ b/serverside_challenge_2/challenge/config/credentials.yml.enc @@ -1 +1 @@ -bZE5VzGsIQoeBEOa79I28cq/Ax0SuIaPhy5+NFktLVZLNGpuzqmkTkAiWc+arbGUDU8aaedg9zUvnBBc0dPrATeWISHtFxVwJMScZQeSW1k7BUgSD6B6YsewlgtpEDFDAXoGZEQoTbM2FY3lDCd0dZF7I1+DsB4I9Z5NamOgOlNEK8gMQD3p3Csqc/mVhmdI3PjBEmblvTjkZIVOdfX1CmqJ/RZm1Lxu3RdNMaiXiOZL7I+oXE1SFfjWm7+CTwb+Vfs6nGMviDHDKh9k3jyb7Vflpx/frY4vYzP8WMzgbJPX4UHmxa7qS51tJ1ECpK265nS1mLsqxL7wRbjyzfUNtXknSSopGD7sDDm7F9ta8/e1nzU79uTH1LKFpWQWGMSbXmSu4EKPu655BA9ckdwnSsf67RYNX7/Nw5pp--9AT1V0z/SBAdVTZ5--JK/4x776vcnN7qGZuylM0g== \ No newline at end of file +d6BqnWkKEyrqPvcoCYBsp3NT3P/g/WNzclXV/4DOXll+ID8kJyPnNAbceJQhJ2krh1O8YbhtiNM1TmftLPH9xVJ0otG8BSOBWc36xPdEN3uR8ivP9w1j3mpKR8i8j8RPdJi8J+L87lNbsmrGHzpweJRaykxLeILLu/3RiYtp3h1MSut9MjF8XXIyhegEQxe7rWWbHkqlvRS8aeM+Qt3ZgT7AxI9PyONogtoAbC23oVIAl4i/JCWeaBxjBAuYoniiRT+mUdd5CszkOlyLl0ugPEdbxNX1wJFgsqHE/gbHit/RGt+nRbIqVgQu9yNC/k9KUOJvh8vYS4URKJcZDPsWJ67wWXdRAsIzKduY9ObF5bVYUDb8JQpbpzmuK4gAu4GbJK/5uxAQIva/Akpn6yNP5O9/6bDrw1VoITGeIYZkg624GjrwjEzz2bDcRYKlgMSXuHuUbONW6vnhLcklqOn/b9RNzIBzvL3igoHVBP9zU3pWFVULb7lI9rtdTsXuhsWdY5JcdhsxAX7aLPE0liioczUJdRQoDj+p+A9SdcOZy3JppkdtDVoeM6HE7PfD4Ohe+UvQqMissDo1vrkVay9mnlVkMThRaB2Y16hjNdDbhcWB/KUlR5o=--v3Hrv+GhSXHn1jO2--ThCVTGupSRpsV7JAOU2jUg== \ No newline at end of file From 771639cbc6067b8148e187dcfa9f30502669052d Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Tue, 22 Jul 2025 01:26:22 +0900 Subject: [PATCH 08/33] update secret_key_base for production --- serverside_challenge_2/challenge/config/credentials.yml.enc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/config/credentials.yml.enc b/serverside_challenge_2/challenge/config/credentials.yml.enc index 690711894..3530d435b 100644 --- a/serverside_challenge_2/challenge/config/credentials.yml.enc +++ b/serverside_challenge_2/challenge/config/credentials.yml.enc @@ -1 +1 @@ -d6BqnWkKEyrqPvcoCYBsp3NT3P/g/WNzclXV/4DOXll+ID8kJyPnNAbceJQhJ2krh1O8YbhtiNM1TmftLPH9xVJ0otG8BSOBWc36xPdEN3uR8ivP9w1j3mpKR8i8j8RPdJi8J+L87lNbsmrGHzpweJRaykxLeILLu/3RiYtp3h1MSut9MjF8XXIyhegEQxe7rWWbHkqlvRS8aeM+Qt3ZgT7AxI9PyONogtoAbC23oVIAl4i/JCWeaBxjBAuYoniiRT+mUdd5CszkOlyLl0ugPEdbxNX1wJFgsqHE/gbHit/RGt+nRbIqVgQu9yNC/k9KUOJvh8vYS4URKJcZDPsWJ67wWXdRAsIzKduY9ObF5bVYUDb8JQpbpzmuK4gAu4GbJK/5uxAQIva/Akpn6yNP5O9/6bDrw1VoITGeIYZkg624GjrwjEzz2bDcRYKlgMSXuHuUbONW6vnhLcklqOn/b9RNzIBzvL3igoHVBP9zU3pWFVULb7lI9rtdTsXuhsWdY5JcdhsxAX7aLPE0liioczUJdRQoDj+p+A9SdcOZy3JppkdtDVoeM6HE7PfD4Ohe+UvQqMissDo1vrkVay9mnlVkMThRaB2Y16hjNdDbhcWB/KUlR5o=--v3Hrv+GhSXHn1jO2--ThCVTGupSRpsV7JAOU2jUg== \ No newline at end of file +aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw== \ No newline at end of file From 46c3d9faa995363c8d0442bf174b20c8433f373e Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Wed, 23 Jul 2025 15:01:22 +0900 Subject: [PATCH 09/33] revise amp validatemethod, correct method name for error processing --- .../api/v1/power_supply_plan_controller.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 19eacb4d0..9dd9b9c37 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -2,8 +2,8 @@ class Api::V1::PowerSupplyPlanController < ApplicationController def simulate_all amp = params[:amp] meter_rate = params[:meter_rate] - validate_result = validate_request(amp, meter_rate) - return render json: validate_result if validate_result.present? + bad_request_result = build_bad_request_resut(amp, meter_rate) + return render json: bad_request_result, status: 400 if bad_request_result.present? amp = amp.to_i meter_rate = meter_rate.to_i @@ -69,12 +69,12 @@ def meter_charge(plan_id, meter_rate) end end - def validate_request(amp, meter_rate) + def build_bad_request_resut(amp, meter_rate) message = "" if amp.blank? message = message + "リクエストパラメータ 'amp' が不足しています。\n" else - message = message + "リクエストパラメータ 'amp' の値が不正です。0以上の整数を指定してください。\n" unless numeric?(amp) && amp.to_i >= 0 + message = message + "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。\n" unless valid_amp?(amp) end if meter_rate.blank? message = message + "リクエストパラメータ 'meter_rate' が不足しています。\n" @@ -96,6 +96,10 @@ def numeric?(string) false end + def valid_amp?(amp) + [10, 15, 20, 30, 40, 50, 60].include?(amp.to_i) + end + def errro_result(result) [ { From ec7f650443755e80ce36398725d54ba26bea728f Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Wed, 23 Jul 2025 17:36:58 +0900 Subject: [PATCH 10/33] add rounding decimal process, separate provider management data from provider data --- .../api/v1/power_supply_plan_controller.rb | 13 +++++++++++-- .../challenge/config/data/power_supply_plans.csv | 10 +++++----- .../challenge/config/data/providers.csv | 4 ++++ 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 serverside_challenge_2/challenge/config/data/providers.csv diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 9dd9b9c37..630e736bc 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -23,9 +23,9 @@ def simulate_all end result << { - provider_name: plan.provider_name.to_s, + provider_name: Provider.find(plan.provider_id.to_s).provider_name, plan_name: plan.plan_name.to_s, - price: (basic_charge_price + meter_charge_price).round + price: round_expense((basic_charge_price + meter_charge_price), plan) } end @@ -34,6 +34,15 @@ def simulate_all private +def round_expense(price, plan) + round_method = Provider.find(plan.provider_id.to_s).rounding_decimal_points + if round_method == "round" + return price.round(0) + elsif round_method == "off" + return price.floor(0) + end +end + def basic_charge(plan_id, amp) basic_charge_record = BasicCharge.where(plan_id: plan_id, amp: amp) if basic_charge_record.count == 1 diff --git a/serverside_challenge_2/challenge/config/data/power_supply_plans.csv b/serverside_challenge_2/challenge/config/data/power_supply_plans.csv index 042a8bc32..10b207a23 100644 --- a/serverside_challenge_2/challenge/config/data/power_supply_plans.csv +++ b/serverside_challenge_2/challenge/config/data/power_supply_plans.csv @@ -1,5 +1,5 @@ -id,provider_name,plan_name -1,東京電力エナジーパートナー,従量電灯B -2,東京電力エナジーパートナー,スタンダードS -3,東京ガス,ずっとも電気1 -4,Looopでんき,おうちプラン \ No newline at end of file +id,provider_id,plan_name +1,1,従量電灯B +2,1,スタンダードS +3,2,ずっとも電気1 +4,3,おうちプラン \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/providers.csv b/serverside_challenge_2/challenge/config/data/providers.csv new file mode 100644 index 000000000..d3b42d8b4 --- /dev/null +++ b/serverside_challenge_2/challenge/config/data/providers.csv @@ -0,0 +1,4 @@ +id,provider_name,rounding_decimal_points +1,東京電力エナジーパートナー,off +2,東京ガス,off +3,Looopでんき,round \ No newline at end of file From 4cc5b0eeed3a8dac8e891312355d0833391a9372 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Wed, 23 Jul 2025 17:41:01 +0900 Subject: [PATCH 11/33] add provider model --- serverside_challenge_2/challenge/app/models/provider.rb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 serverside_challenge_2/challenge/app/models/provider.rb diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb new file mode 100644 index 000000000..cd56a91ae --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -0,0 +1,6 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class Provider < ActiveHash::Base + include ActiveHash::Associations + self.data = CSV.read('config/data/providers.csv', headers: true).map(&:to_hash) +end \ No newline at end of file From 8eb6b5cbdee775620561b8bb390d0c0e3e6bf21d Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 13:02:54 +0900 Subject: [PATCH 12/33] create migration, seed file, executed migration --- .../20250723134304_create_basic_charges.rb | 34 ++++++ serverside_challenge_2/challenge/db/schema.rb | 51 +++++++++ serverside_challenge_2/challenge/db/seeds.rb | 104 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 serverside_challenge_2/challenge/db/migrate/20250723134304_create_basic_charges.rb create mode 100644 serverside_challenge_2/challenge/db/schema.rb diff --git a/serverside_challenge_2/challenge/db/migrate/20250723134304_create_basic_charges.rb b/serverside_challenge_2/challenge/db/migrate/20250723134304_create_basic_charges.rb new file mode 100644 index 000000000..8b9371f43 --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250723134304_create_basic_charges.rb @@ -0,0 +1,34 @@ +class CreateBasicCharges < ActiveRecord::Migration[7.0] + def change + create_table :providers do |t| + t.string :provider_name, :null => false + t.string :rounding_amount_method, :null => false + + t.timestamps + end + + create_table :power_supply_plans do |t| + t.belongs_to :provider + t.string :plan_name, :null => false + + t.timestamps + end + + create_table :meter_rate_charges do |t| + t.belongs_to :power_supply_plan + t.integer :min_meter_rate, :null => false + t.integer :max_meter_rate + t.decimal :price, precision: 6, scale: 2, :null => false + + t.timestamps + end + + create_table :basic_charges do |t| + t.belongs_to :power_supply_plan + t.integer :amp, :null => false + t.decimal :price, precision: 6, scale: 2, :null => false + + t.timestamps + end + end +end diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb new file mode 100644 index 000000000..b694cc928 --- /dev/null +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -0,0 +1,51 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2025_07_23_134304) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "basic_charges", force: :cascade do |t| + t.bigint "power_supply_plan_id" + t.integer "amp", null: false + t.decimal "price", precision: 6, scale: 2, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["power_supply_plan_id"], name: "index_basic_charges_on_power_supply_plan_id" + end + + create_table "meter_rate_charges", force: :cascade do |t| + t.bigint "power_supply_plan_id" + t.integer "min_meter_rate", null: false + t.integer "max_meter_rate" + t.decimal "price", precision: 6, scale: 2, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["power_supply_plan_id"], name: "index_meter_rate_charges_on_power_supply_plan_id" + end + + create_table "power_supply_plans", force: :cascade do |t| + t.bigint "provider_id" + t.string "plan_name", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["provider_id"], name: "index_power_supply_plans_on_provider_id" + end + + create_table "providers", force: :cascade do |t| + t.string "provider_name", null: false + t.string "rounding_amount_method", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index bc25fce30..9a2fa492d 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -5,3 +5,107 @@ # # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) # Character.create(name: "Luke", movie: movies.first) + +p '==================== provider create ====================' + +Provider.create! ( + [ + { provider_name: "東京電力エナジーパートナー", rounding_amount_method: "off" }, + { provider_name: "東京ガス", rounding_amount_method: "off" }, + { provider_name: "Looopでんき", rounding_amount_method: "round" } + ] +) + +p '==================== power_supply_plan create ====================' + +provider = Provider.find(1) +plans = provider.power_supply_plans.new( + [ + {plan_name: "従量電灯B"}, + {plan_name: "スタンダードS"} + ] +) +plans.each { |plan| plan.save } + +provider = Provider.find(2).power_supply_plans.create!(plan_name: "ずっとも電気1") +provider = Provider.find(3).power_supply_plans.create!(plan_name: "おうちプラン") + +p '==================== basic_charge create ====================' + +seed_data = [ + {index: 1, data: [ + {amp: 10, price: 286.00}, + {amp: 15, price: 429.00}, + {amp: 20, price: 572.00}, + {amp: 30, price: 858.00}, + {amp: 40, price: 1144.00}, + {amp: 50, price: 1430.00} + ] + }, + {index: 2, data: [ + {amp: 10, price: 311.75}, + {amp: 15, price: 467.63}, + {amp: 20, price: 623.50}, + {amp: 30, price: 935.25}, + {amp: 40, price: 1247.00}, + {amp: 50, price: 1558.75}, + {amp: 60, price: 1870.50} + ] + }, + {index: 3, data: [ + {amp: 30, price: 858.00}, + {amp: 40, price: 1144.00}, + {amp: 50, price: 1430.00}, + {amp: 60, price: 1716.00} + ] + }, + {index: 4, data: [ + {amp: 10, price: 0.00}, + {amp: 15, price: 0.00}, + {amp: 20, price: 0.00}, + {amp: 30, price: 0.00}, + {amp: 40, price: 0.00}, + {amp: 50, price: 0.00}, + {amp: 60, price: 0.00} + ] + } +] +seed_data.each do |d| + plan = PowerSupplyPlan.find(d[:index]) + basic_charges = plan.basic_charges.new( d[:data]) + basic_charges.each { |charge| charge.save } +end + +p '==================== meter_ratecharge create ====================' + +seed_data = [ + {index: 1, data: [ + {min_meter_rate: 0, max_meter_rate: 120, price: 19.88}, + {min_meter_rate: 121, max_meter_rate: 300, price: 26.48}, + {min_meter_rate: 301, max_meter_rate: nil, price: 30.57} + ] + }, + {index: 2, data: [ + {min_meter_rate: 0, max_meter_rate: 120, price: 29.80}, + {min_meter_rate: 121, max_meter_rate: 300, price: 36.40}, + {min_meter_rate: 301, max_meter_rate: nil, price: 40.49} + ] + }, + {index: 3, data: [ + {min_meter_rate: 0, max_meter_rate: 140, price: 23.67}, + {min_meter_rate: 141, max_meter_rate: 350, price: 23.88}, + {min_meter_rate: 351, max_meter_rate: nil, price: 26.41} + ] + }, + {index: 4, data: [ + {min_meter_rate: 0, max_meter_rate: nil, price: 28.80} + ] + } +] + +seed_data.each do |d| + plan = PowerSupplyPlan.find(d[:index]) + basic_charges = plan.meter_rate_charges.new( d[:data]) + basic_charges.each { |charge| charge.save } +end + From e897b3c6b697d558705c5a899163fc2ca9359b56 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 14:19:10 +0900 Subject: [PATCH 13/33] rename existing model with 'csv' suffix, add provider model --- .../api/v1/power_supply_plan_controller.rb | 12 +++++----- .../challenge/app/models/basic_charge_csv.rb | 21 +++++++++++++++++ .../app/models/meter_rate_charge_csv.rb | 23 +++++++++++++++++++ .../app/models/power_supply_plan_csv.rb | 6 +++++ .../challenge/app/models/provider_csv.rb | 6 +++++ .../challenge/config/data/providers.csv | 2 +- 6 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 serverside_challenge_2/challenge/app/models/basic_charge_csv.rb create mode 100644 serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb create mode 100644 serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb create mode 100644 serverside_challenge_2/challenge/app/models/provider_csv.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 630e736bc..1fb587bb8 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -9,7 +9,7 @@ def simulate_all meter_rate = meter_rate.to_i # 各プランの基本料金を取得 - plans = PowerSupplyPlan.all + plans = PowerSupplyPlanCsv.all result = [] for plan in plans do id = plan.id @@ -23,7 +23,7 @@ def simulate_all end result << { - provider_name: Provider.find(plan.provider_id.to_s).provider_name, + provider_name: ProviderCsv.find(plan.provider_id.to_s).provider_name, plan_name: plan.plan_name.to_s, price: round_expense((basic_charge_price + meter_charge_price), plan) } @@ -35,7 +35,7 @@ def simulate_all private def round_expense(price, plan) - round_method = Provider.find(plan.provider_id.to_s).rounding_decimal_points + round_method = ProviderCsv.find(plan.provider_id.to_s).rounding_amount_method if round_method == "round" return price.round(0) elsif round_method == "off" @@ -44,7 +44,7 @@ def round_expense(price, plan) end def basic_charge(plan_id, amp) - basic_charge_record = BasicCharge.where(plan_id: plan_id, amp: amp) + basic_charge_record = BasicChargeCsv.where(plan_id: plan_id, amp: amp) if basic_charge_record.count == 1 price = basic_charge_record.first.price elsif basic_charge_record.count == 0 @@ -55,7 +55,7 @@ def basic_charge(plan_id, amp) end def meter_charge(plan_id, meter_rate) - plan = MeterRateCharge.where( + plan = MeterRateChargeCsv.where( plan_id: plan_id, min_meter_rate: ..meter_rate, max_meter_rate: meter_rate.. @@ -63,7 +63,7 @@ def meter_charge(plan_id, meter_rate) logger.debug "plan.bank?: #{plan.blank?}" if plan.blank? - maximum_plan = MeterRateCharge.where( + maximum_plan = MeterRateChargeCsv.where( plan_id: plan_id, max_meter_rate: nil ) diff --git a/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb b/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb new file mode 100644 index 000000000..36e67ea45 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb @@ -0,0 +1,21 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class BasicChargeCsv < ActiveHash::Base + include ActiveHash::Associations + parsed_data = [] + data = CSV.read('config/data/basic_charges.csv', headers: true).map(&:to_hash) + data.each do |row| + id = row["id"].to_i + plan_id = row["plan_id"].to_i + amp = row["amp"].to_i + price = row["price"].to_f + puts row.to_h + parsed_data << { + id: id, + plan_id: plan_id, + amp: amp, + price: price + } + end + self.data = parsed_data +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb new file mode 100644 index 000000000..21251a3b9 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb @@ -0,0 +1,23 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class MeterRateChargeCsv < ActiveHash::Base + include ActiveHash::Associations + parsed_data = [] + data = CSV.read('config/data/meter_rate_charges.csv', headers: true).map(&:to_hash) + data.each do |row| + id = row["id"].to_i + plan_id = row["plan_id"].to_i + min_meter_rate = row["min_meter_rate"].to_i + max_meter_rate = row["max_meter_rate"].nil? ? nil : row["max_meter_rate"].to_i + price = row["price"].to_f + puts row.to_h + parsed_data << { + id: id, + plan_id: plan_id, + min_meter_rate: min_meter_rate, + max_meter_rate: max_meter_rate, + price: price + } + end + self.data = parsed_data +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb new file mode 100644 index 000000000..d8ff638f3 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb @@ -0,0 +1,6 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class PowerSupplyPlanCsv < ActiveHash::Base + include ActiveHash::Associations + self.data = CSV.read('config/data/power_supply_plans.csv', headers: true).map(&:to_hash) +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/provider_csv.rb b/serverside_challenge_2/challenge/app/models/provider_csv.rb new file mode 100644 index 000000000..af3324459 --- /dev/null +++ b/serverside_challenge_2/challenge/app/models/provider_csv.rb @@ -0,0 +1,6 @@ +require 'csv' # CSVファイルを扱うためにCSVライブラリをロード + +class ProviderCsv < ActiveHash::Base + include ActiveHash::Associations + self.data = CSV.read('config/data/providers.csv', headers: true).map(&:to_hash) +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/providers.csv b/serverside_challenge_2/challenge/config/data/providers.csv index d3b42d8b4..b5dad0e74 100644 --- a/serverside_challenge_2/challenge/config/data/providers.csv +++ b/serverside_challenge_2/challenge/config/data/providers.csv @@ -1,4 +1,4 @@ -id,provider_name,rounding_decimal_points +id,provider_name,rounding_amount_method 1,東京電力エナジーパートナー,off 2,東京ガス,off 3,Looopでんき,round \ No newline at end of file From f53519863f847ca105f8812ee82d020fb55241aa Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 14:20:43 +0900 Subject: [PATCH 14/33] add model for db management --- .../challenge/app/models/basic_charge.rb | 24 +++-------------- .../challenge/app/models/meter_rate_charge.rb | 26 +++---------------- .../challenge/app/models/power_supply_plan.rb | 11 ++++---- .../challenge/app/models/provider.rb | 9 +++---- 4 files changed, 14 insertions(+), 56 deletions(-) diff --git a/serverside_challenge_2/challenge/app/models/basic_charge.rb b/serverside_challenge_2/challenge/app/models/basic_charge.rb index 07d1a1fdc..93075bca3 100644 --- a/serverside_challenge_2/challenge/app/models/basic_charge.rb +++ b/serverside_challenge_2/challenge/app/models/basic_charge.rb @@ -1,21 +1,3 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class BasicCharge < ActiveHash::Base - include ActiveHash::Associations - parsed_data = [] - data = CSV.read('config/data/basic_charges.csv', headers: true).map(&:to_hash) - data.each do |row| - id = row["id"].to_i - plan_id = row["plan_id"].to_i - amp = row["amp"].to_i - price = row["price"].to_f - puts row.to_h - parsed_data << { - id: id, - plan_id: plan_id, - amp: amp, - price: price - } - end - self.data = parsed_data -end \ No newline at end of file +class BasicCharge < ApplicationRecord + belongs_to :power_supply_plan +end diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb index 7625b3cfc..35566461d 100644 --- a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb +++ b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb @@ -1,23 +1,3 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class MeterRateCharge < ActiveHash::Base - include ActiveHash::Associations - parsed_data = [] - data = CSV.read('config/data/meter_rate_charges.csv', headers: true).map(&:to_hash) - data.each do |row| - id = row["id"].to_i - plan_id = row["plan_id"].to_i - min_meter_rate = row["min_meter_rate"].to_i - max_meter_rate = row["max_meter_rate"].nil? ? nil : row["max_meter_rate"].to_i - price = row["price"].to_f - puts row.to_h - parsed_data << { - id: id, - plan_id: plan_id, - min_meter_rate: min_meter_rate, - max_meter_rate: max_meter_rate, - price: price - } - end - self.data = parsed_data -end \ No newline at end of file +class MeterRateCharge < ApplicationRecord + belongs_to :power_supply_plan +end diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb index bbfaf301f..756115e1f 100644 --- a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb @@ -1,6 +1,5 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class PowerSupplyPlan < ActiveHash::Base - include ActiveHash::Associations - self.data = CSV.read('config/data/power_supply_plans.csv', headers: true).map(&:to_hash) -end \ No newline at end of file +class PowerSupplyPlan < ApplicationRecord + belongs_to :provider + has_many :meter_rate_charges + has_many :basic_charges +end diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb index cd56a91ae..bbe211ced 100644 --- a/serverside_challenge_2/challenge/app/models/provider.rb +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -1,6 +1,3 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class Provider < ActiveHash::Base - include ActiveHash::Associations - self.data = CSV.read('config/data/providers.csv', headers: true).map(&:to_hash) -end \ No newline at end of file +class Provider < ApplicationRecord + has_many :power_supply_plans +end From 2371165df8bc21376a908876e6808e6962d3a098 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 17:02:00 +0900 Subject: [PATCH 15/33] correct seed.rb --- serverside_challenge_2/challenge/db/seeds.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index 9a2fa492d..a13066f40 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -39,7 +39,8 @@ {amp: 20, price: 572.00}, {amp: 30, price: 858.00}, {amp: 40, price: 1144.00}, - {amp: 50, price: 1430.00} + {amp: 50, price: 1430.00}, + {amp: 60, price: 1716.00} ] }, {index: 2, data: [ From 061e0191e76da9f3fbc1c7b4d74475438f6f9603 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 17:09:20 +0900 Subject: [PATCH 16/33] change data reference from CSV to DB on processing calculation --- .../api/v1/power_supply_plan_controller.rb | 72 ++++++++----------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 1fb587bb8..0d30dacd5 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -9,23 +9,19 @@ def simulate_all meter_rate = meter_rate.to_i # 各プランの基本料金を取得 - plans = PowerSupplyPlanCsv.all + plans = PowerSupplyPlan.all result = [] for plan in plans do - id = plan.id - basic_charge_price = basic_charge(id, amp) - if basic_charge_price == -1 - next - end - meter_charge_price = meter_charge(id, meter_rate) - if meter_charge_price == -1 - next - end + basic_charge = plan.basic_charges.where(amp: amp) + next if basic_charge.blank? + + meter_charge_price = meter_charge(plan, meter_rate) + next if meter_charge_price < 0 result << { - provider_name: ProviderCsv.find(plan.provider_id.to_s).provider_name, + provider_name: plan.provider.provider_name, plan_name: plan.plan_name.to_s, - price: round_expense((basic_charge_price + meter_charge_price), plan) + price: round_expense((basic_charge.first.price.to_f + meter_charge_price), plan) } end @@ -34,47 +30,35 @@ def simulate_all private -def round_expense(price, plan) - round_method = ProviderCsv.find(plan.provider_id.to_s).rounding_amount_method - if round_method == "round" - return price.round(0) - elsif round_method == "off" - return price.floor(0) - end -end + def round_expense(price, plan) + round_method = plan.provider.rounding_amount_method + if round_method == "round" - def basic_charge(plan_id, amp) - basic_charge_record = BasicChargeCsv.where(plan_id: plan_id, amp: amp) - if basic_charge_record.count == 1 - price = basic_charge_record.first.price - elsif basic_charge_record.count == 0 - return -1 + return price.round(0).to_i + elsif round_method == "off" + return price.floor(0).to_i end - - return price end - def meter_charge(plan_id, meter_rate) - plan = MeterRateChargeCsv.where( - plan_id: plan_id, - min_meter_rate: ..meter_rate, - max_meter_rate: meter_rate.. - ) - logger.debug "plan.bank?: #{plan.blank?}" + def meter_charge(plan, meter_rate) + result_plan = plan.meter_rate_charges.where( + min_meter_rate: ..meter_rate, + max_meter_rate: meter_rate.. + ) - if plan.blank? - maximum_plan = MeterRateChargeCsv.where( - plan_id: plan_id, - max_meter_rate: nil - ) - if maximum_plan.first.min_meter_rate <= meter_rate - return maximum_plan.first.price * meter_rate.to_f - else + if result_plan.blank? + maximum_plan = plan.meter_rate_charges.where( + min_meter_rate: ..meter_rate, + max_meter_rate: nil + ) + if maximum_plan.blank? return -1 + else + maximum_plan.first.price * meter_rate.to_f end else - return plan.first.price * meter_rate.to_f + return result_plan.first.price * meter_rate.to_f end end From 2cdbfd2717e9279360606e8f562a69ed5f6f2175 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 17:50:39 +0900 Subject: [PATCH 17/33] add rubocop --- serverside_challenge_2/challenge/.rubocop.yml | 9 +++++ serverside_challenge_2/challenge/Gemfile | 1 + serverside_challenge_2/challenge/Gemfile.lock | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 serverside_challenge_2/challenge/.rubocop.yml diff --git a/serverside_challenge_2/challenge/.rubocop.yml b/serverside_challenge_2/challenge/.rubocop.yml new file mode 100644 index 000000000..1ef5d7981 --- /dev/null +++ b/serverside_challenge_2/challenge/.rubocop.yml @@ -0,0 +1,9 @@ +Documentation: + Enabled: false +MethodLength: + CountComments: true + Max: 25 + Style/FrozenStringLiteralComment: + Enabled: false +Metrics: + Enabled: false \ No newline at end of file diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index bc3972329..fdcd4135e 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -37,6 +37,7 @@ gem "bootsnap", require: false # gem "rack-cors" gem "active_hash" +gem 'rubocop-rails', '~> 2.32' group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index 20720c885..87150bfaf 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -68,6 +68,7 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + ast (2.4.3) bootsnap (1.18.3) msgpack (~> 1.2) builder (3.2.4) @@ -86,6 +87,9 @@ GEM irb (1.11.2) rdoc reline (>= 0.4.2) + json (2.13.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -113,7 +117,12 @@ GEM racc (~> 1.4) nokogiri (1.16.2-x86_64-linux) racc (~> 1.4) + parallel (1.27.0) + parser (3.3.8.0) + ast (~> 2.4.1) + racc pg (1.5.4) + prism (1.4.0) psych (5.1.2) stringio puma (5.6.8) @@ -150,16 +159,42 @@ GEM rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) + rainbow (3.1.1) rake (13.1.0) rdoc (6.6.2) psych (>= 4.0.0) + regexp_parser (2.10.0) reline (0.4.2) io-console (~> 0.5) + rubocop (1.78.0) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.45.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-rails (2.32.0) + activesupport (>= 4.2.0) + lint_roller (~> 1.1) + rack (>= 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-progressbar (1.13.0) stringio (3.1.0) thor (1.3.0) timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -176,6 +211,7 @@ DEPENDENCIES pg (~> 1.1) puma (~> 5.0) rails (~> 7.0.8) + rubocop-rails (~> 2.32) tzinfo-data RUBY VERSION From 5909b8b958262902f445fa240e66a67117033cde Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 17:51:52 +0900 Subject: [PATCH 18/33] rubocop code correction --- .../api/v1/power_supply_plan_controller.rb | 179 +++++++++--------- .../challenge/app/models/basic_charge.rb | 2 + .../challenge/app/models/meter_rate_charge.rb | 2 + .../challenge/app/models/power_supply_plan.rb | 2 + .../challenge/app/models/provider.rb | 2 + 5 files changed, 96 insertions(+), 91 deletions(-) diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 0d30dacd5..517ea1ba5 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -1,106 +1,103 @@ -class Api::V1::PowerSupplyPlanController < ApplicationController - def simulate_all - amp = params[:amp] - meter_rate = params[:meter_rate] - bad_request_result = build_bad_request_resut(amp, meter_rate) - return render json: bad_request_result, status: 400 if bad_request_result.present? - - amp = amp.to_i - meter_rate = meter_rate.to_i - - # 各プランの基本料金を取得 - plans = PowerSupplyPlan.all - result = [] - for plan in plans do - basic_charge = plan.basic_charges.where(amp: amp) - next if basic_charge.blank? +# frozen_string_literal: true - meter_charge_price = meter_charge(plan, meter_rate) - next if meter_charge_price < 0 - - result << { - provider_name: plan.provider.provider_name, - plan_name: plan.plan_name.to_s, - price: round_expense((basic_charge.first.price.to_f + meter_charge_price), plan) - } - end - - render json: result - end - - private +module Api + module V1 + class PowerSupplyPlanController < ApplicationController + def simulate_all + amp = params[:amp] + meter_rate = params[:meter_rate] + bad_request_result = build_200_message(amp, meter_rate) + return render json: bad_request_result, status: 400 if bad_request_result.present? - def round_expense(price, plan) - round_method = plan.provider.rounding_amount_method - if round_method == "round" + amp = amp.to_i + meter_rate = meter_rate.to_i + plans = PowerSupplyPlan.all + result = [] + plans.each do |plan| + basic_charge = plan.basic_charges.where(amp: amp) + next if basic_charge.blank? - return price.round(0).to_i - elsif round_method == "off" - return price.floor(0).to_i - end - end + meter_charge_price = meter_charge(plan, meter_rate) + next if meter_charge_price.negative? - def meter_charge(plan, meter_rate) - result_plan = plan.meter_rate_charges.where( - min_meter_rate: ..meter_rate, - max_meter_rate: meter_rate.. - ) + result << { + provider_name: plan.provider.provider_name, + plan_name: plan.plan_name.to_s, + price: round_expense(basic_charge.first.price.to_f + meter_charge_price, plan) + } + end + render json: result + end - if result_plan.blank? - maximum_plan = plan.meter_rate_charges.where( - min_meter_rate: ..meter_rate, - max_meter_rate: nil - ) - if maximum_plan.blank? - return -1 - else - maximum_plan.first.price * meter_rate.to_f + private + + def round_expense(price, plan) + round_method = plan.provider.rounding_amount_method + if round_method == 'round' + price.round(0).to_i + elsif round_method == 'off' + price.floor(0).to_i + end end - else - return result_plan.first.price * meter_rate.to_f - end - end + def meter_charge(plan, meter_rate) + result_plan = plan.meter_rate_charges.where( + min_meter_rate: ..meter_rate, + max_meter_rate: meter_rate.. + ) + return result_plan.first.price * meter_rate.to_f unless result_plan.blank? - def build_bad_request_resut(amp, meter_rate) - message = "" - if amp.blank? - message = message + "リクエストパラメータ 'amp' が不足しています。\n" - else - message = message + "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。\n" unless valid_amp?(amp) - end - if meter_rate.blank? - message = message + "リクエストパラメータ 'meter_rate' が不足しています。\n" - else - message = message + "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。\n" unless numeric?(meter_rate) && meter_rate.to_i >= 0 - end + maximum_plan = plan.meter_rate_charges.where( + min_meter_rate: ..meter_rate, + max_meter_rate: nil + ) + return -1 if maximum_plan.blank? - if message.present? - return errro_result(message) - end + maximum_plan.first.price * meter_rate.to_f + end - return [] - end + def build_200_message(amp, meter_rate) + message = '' + if amp.blank? + message += "リクエストパラメータ 'amp' が不足しています。\n" + else + unless valid_amp?(amp) + message += "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。\n" + end + end + if meter_rate.blank? + message += "リクエストパラメータ 'meter_rate' が不足しています。\n" + else + unless numeric?(meter_rate) && meter_rate.to_i >= 0 + message += "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。\n" + end + end + return errro_result(message) if message.present? - def numeric?(string) - Integer(string) - true - rescue ArgumentError, TypeError - false - end + [] + end - def valid_amp?(amp) - [10, 15, 20, 30, 40, 50, 60].include?(amp.to_i) - end + def numeric?(string) + Integer(string) + true + rescue ArgumentError, TypeError + false + end + + def valid_amp?(amp) + [10, 15, 20, 30, 40, 50, 60].include?(amp.to_i) + end - def errro_result(result) - [ - { - error: { - code: 400, - message: result - } - } - ] + def errro_result(result) + [ + { + error: { + code: 400, + message: result + } + } + ] + end + end end end diff --git a/serverside_challenge_2/challenge/app/models/basic_charge.rb b/serverside_challenge_2/challenge/app/models/basic_charge.rb index 93075bca3..a7c0f9e58 100644 --- a/serverside_challenge_2/challenge/app/models/basic_charge.rb +++ b/serverside_challenge_2/challenge/app/models/basic_charge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BasicCharge < ApplicationRecord belongs_to :power_supply_plan end diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb index 35566461d..b1007e517 100644 --- a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb +++ b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MeterRateCharge < ApplicationRecord belongs_to :power_supply_plan end diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb index 756115e1f..0a51bb519 100644 --- a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PowerSupplyPlan < ApplicationRecord belongs_to :provider has_many :meter_rate_charges diff --git a/serverside_challenge_2/challenge/app/models/provider.rb b/serverside_challenge_2/challenge/app/models/provider.rb index bbe211ced..445f24073 100644 --- a/serverside_challenge_2/challenge/app/models/provider.rb +++ b/serverside_challenge_2/challenge/app/models/provider.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Provider < ApplicationRecord has_many :power_supply_plans end From 78237d904fadae594a3f7a0b4a259b4f6bc9b8ab Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 18:01:20 +0900 Subject: [PATCH 19/33] remove csv data and models --- .../challenge/app/models/basic_charge_csv.rb | 21 --------------- .../app/models/meter_rate_charge_csv.rb | 23 ---------------- .../app/models/power_supply_plan_csv.rb | 6 ----- .../challenge/app/models/provider_csv.rb | 6 ----- .../challenge/config/data/basic_charges.csv | 26 ------------------- .../config/data/meter_rate_charges.csv | 11 -------- .../config/data/power_supply_plans.csv | 5 ---- .../challenge/config/data/providers.csv | 4 --- 8 files changed, 102 deletions(-) delete mode 100644 serverside_challenge_2/challenge/app/models/basic_charge_csv.rb delete mode 100644 serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb delete mode 100644 serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb delete mode 100644 serverside_challenge_2/challenge/app/models/provider_csv.rb delete mode 100644 serverside_challenge_2/challenge/config/data/basic_charges.csv delete mode 100644 serverside_challenge_2/challenge/config/data/meter_rate_charges.csv delete mode 100644 serverside_challenge_2/challenge/config/data/power_supply_plans.csv delete mode 100644 serverside_challenge_2/challenge/config/data/providers.csv diff --git a/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb b/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb deleted file mode 100644 index 36e67ea45..000000000 --- a/serverside_challenge_2/challenge/app/models/basic_charge_csv.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class BasicChargeCsv < ActiveHash::Base - include ActiveHash::Associations - parsed_data = [] - data = CSV.read('config/data/basic_charges.csv', headers: true).map(&:to_hash) - data.each do |row| - id = row["id"].to_i - plan_id = row["plan_id"].to_i - amp = row["amp"].to_i - price = row["price"].to_f - puts row.to_h - parsed_data << { - id: id, - plan_id: plan_id, - amp: amp, - price: price - } - end - self.data = parsed_data -end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb deleted file mode 100644 index 21251a3b9..000000000 --- a/serverside_challenge_2/challenge/app/models/meter_rate_charge_csv.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class MeterRateChargeCsv < ActiveHash::Base - include ActiveHash::Associations - parsed_data = [] - data = CSV.read('config/data/meter_rate_charges.csv', headers: true).map(&:to_hash) - data.each do |row| - id = row["id"].to_i - plan_id = row["plan_id"].to_i - min_meter_rate = row["min_meter_rate"].to_i - max_meter_rate = row["max_meter_rate"].nil? ? nil : row["max_meter_rate"].to_i - price = row["price"].to_f - puts row.to_h - parsed_data << { - id: id, - plan_id: plan_id, - min_meter_rate: min_meter_rate, - max_meter_rate: max_meter_rate, - price: price - } - end - self.data = parsed_data -end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb deleted file mode 100644 index d8ff638f3..000000000 --- a/serverside_challenge_2/challenge/app/models/power_supply_plan_csv.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class PowerSupplyPlanCsv < ActiveHash::Base - include ActiveHash::Associations - self.data = CSV.read('config/data/power_supply_plans.csv', headers: true).map(&:to_hash) -end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/app/models/provider_csv.rb b/serverside_challenge_2/challenge/app/models/provider_csv.rb deleted file mode 100644 index af3324459..000000000 --- a/serverside_challenge_2/challenge/app/models/provider_csv.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'csv' # CSVファイルを扱うためにCSVライブラリをロード - -class ProviderCsv < ActiveHash::Base - include ActiveHash::Associations - self.data = CSV.read('config/data/providers.csv', headers: true).map(&:to_hash) -end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/basic_charges.csv b/serverside_challenge_2/challenge/config/data/basic_charges.csv deleted file mode 100644 index 542c906b6..000000000 --- a/serverside_challenge_2/challenge/config/data/basic_charges.csv +++ /dev/null @@ -1,26 +0,0 @@ -id,plan_id,amp,price -1,1,10,286.00 -2,1,15,429.00 -3,1,20,572.00 -4,1,30,858.00 -5,1,40,1144.00 -6,1,50,1430.00 -7,1,60,1716.00 -8,2,10,311.75 -9,2,15,467.63 -10,2,20,623.50 -11,2,30,935.25 -12,2,40,1247.00 -13,2,50,1558.75 -14,2,60,1870.50 -15,3,30,858.00 -16,3,40,1144.00 -17,3,50,1430.00 -18,3,60,1716.00 -19,4,10,0.00 -20,4,15,0.00 -21,4,20,0.00 -22,4,30,0.00 -23,4,40,0.00 -24,4,50,0.00 -25,4,60,0.00 \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv b/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv deleted file mode 100644 index 2aa4fffae..000000000 --- a/serverside_challenge_2/challenge/config/data/meter_rate_charges.csv +++ /dev/null @@ -1,11 +0,0 @@ -id,plan_id,min_meter_rate,max_meter_rate,price -1,1,0,120,19.88 -2,1,121,300,26.48 -3,1,301,,30.57 -4,2,0,120,29.80 -5,2,121,300,36.40 -6,2,301,,40.49 -7,3,0,140,23.67 -8,3,141,350,23.88 -9,3,351,,26.41 -10,4,0,,28.8 \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/power_supply_plans.csv b/serverside_challenge_2/challenge/config/data/power_supply_plans.csv deleted file mode 100644 index 10b207a23..000000000 --- a/serverside_challenge_2/challenge/config/data/power_supply_plans.csv +++ /dev/null @@ -1,5 +0,0 @@ -id,provider_id,plan_name -1,1,従量電灯B -2,1,スタンダードS -3,2,ずっとも電気1 -4,3,おうちプラン \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/data/providers.csv b/serverside_challenge_2/challenge/config/data/providers.csv deleted file mode 100644 index b5dad0e74..000000000 --- a/serverside_challenge_2/challenge/config/data/providers.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,provider_name,rounding_amount_method -1,東京電力エナジーパートナー,off -2,東京ガス,off -3,Looopでんき,round \ No newline at end of file From b9f9e5939d2224d344be00831547589c0c6379d9 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 24 Jul 2025 18:02:08 +0900 Subject: [PATCH 20/33] add empty lines --- serverside_challenge_2/challenge/.rubocop.yml | 3 ++- serverside_challenge_2/challenge/config/credentials.yml.enc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/serverside_challenge_2/challenge/.rubocop.yml b/serverside_challenge_2/challenge/.rubocop.yml index 1ef5d7981..db1c23f63 100644 --- a/serverside_challenge_2/challenge/.rubocop.yml +++ b/serverside_challenge_2/challenge/.rubocop.yml @@ -6,4 +6,5 @@ MethodLength: Style/FrozenStringLiteralComment: Enabled: false Metrics: - Enabled: false \ No newline at end of file + Enabled: false + \ No newline at end of file diff --git a/serverside_challenge_2/challenge/config/credentials.yml.enc b/serverside_challenge_2/challenge/config/credentials.yml.enc index 3530d435b..20d89fc91 100644 --- a/serverside_challenge_2/challenge/config/credentials.yml.enc +++ b/serverside_challenge_2/challenge/config/credentials.yml.enc @@ -1 +1 @@ -aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw== \ No newline at end of file +aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw== From 14325c5e7fc96286b70f2d0123e84c01c612f2f0 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Fri, 25 Jul 2025 23:45:32 +0900 Subject: [PATCH 21/33] restore credentials.yml.enc --- serverside_challenge_2/challenge/config/credentials.yml.enc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/config/credentials.yml.enc b/serverside_challenge_2/challenge/config/credentials.yml.enc index 20d89fc91..3530d435b 100644 --- a/serverside_challenge_2/challenge/config/credentials.yml.enc +++ b/serverside_challenge_2/challenge/config/credentials.yml.enc @@ -1 +1 @@ -aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw== +aLpSiYFmK7YeuO/q62KJR/UyXPpBWhH+oS6rzGWSQH/9p53vCpRMvfaUsZSneP9wYMioohjDqHdeY0bpOAXjMPy5suQk7yGANivapLmd2qNLt1wpIYzKGJiEwSP2gQD4Hyo/K6qGF2fchngDFhy0WWAe8Q0kcIM7WzUSV+VilcYylHcHHpdYhjsBx4+7ev2dbWLlYxbYuFSDGklGP85oYBmIH2QIzcYmwHhBkjwzFWvIl3xqL+/8iQlYsFyBsTk3tntgD/Au3kv6qYXQnf3ITXgHTKlaooxLTy9ZYwMOhJvPwtkMsWv9CVY+ACkUi5h9WY+rSHiBVCDHJwnEhwpz8/n4kzGXLuGRburdDZkA1IZb+j7+KzX625Y+x4+zR7P1wc8vJRw+/5O4MSndI28y0KkX15Qd8vMYNrowsXx+0WFtvo8LTh0J6p84b4bJyVkD1bGUpvpz+UADtGfTbuDqqRXNDCRM7NITVYcOg74QKiwi57gzRJgKOYt1KhXMLA5y4V+xo2zBZuTzGAqNS2rHcfwil9lcsmsTOQL7felWv2C2qqUllUo3CqTGNlXo3A24guP25tr3vDm2IrH7V/TLSjJcDAI1JUi35wwrCErgxrwK9eG0Y4w=--97QWVDZ45R2Bjr3G--byi/t6soITzi5vJv5PEsvw== \ No newline at end of file From e48eade409a687c91b1070ba557660101130e453 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Sat, 26 Jul 2025 01:49:22 +0900 Subject: [PATCH 22/33] correct meter charge amount calculation --- .../api/v1/power_supply_plan_controller.rb | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 517ea1ba5..d0eebd2fc 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -41,19 +41,41 @@ def round_expense(price, plan) end def meter_charge(plan, meter_rate) - result_plan = plan.meter_rate_charges.where( - min_meter_rate: ..meter_rate, - max_meter_rate: meter_rate.. - ) - return result_plan.first.price * meter_rate.to_f unless result_plan.blank? + price = 0 + plan.meter_rate_charges.each do |step| + price += calculate_if_step_applicable(step, meter_rate) + end + price + end - maximum_plan = plan.meter_rate_charges.where( - min_meter_rate: ..meter_rate, - max_meter_rate: nil - ) - return -1 if maximum_plan.blank? + def calculate_if_step_applicable(step, meter_rate) + max_rate = step.max_meter_rate + min_rate = step.min_meter_rate + price = step.price + # 使用量がステップ請求額に満たない場合0を返す + return 0 if meter_rate < min_rate + if max_rate.nil? && min_rate <= meter_rate + # 最大料金ステップの計算 + return (meter_rate - min_rate + 1) * price + end - maximum_plan.first.price * meter_rate.to_f + if min_rate.zero? + # 最小料金ステップの計算 + if max_rate <= meter_rate + # 該当ステップの満額請求計算 + max_rate * price + else + # 該当ステップの使用量までを計算 + meter_rate * price + end + elsif (min_rate..max_rate).include?(meter_rate) + # 中間料金ステップの計算 + # 該当ステップの使用量までを計算 + (meter_rate - min_rate + 1) * price + else + # 該当ステップの満額請求計算 + (max_rate - min_rate + 1) * price + end end def build_200_message(amp, meter_rate) From 56818cc8b59e2aeb44ef70d385f9042bc47a1eba Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Sat, 26 Jul 2025 19:55:39 +0900 Subject: [PATCH 23/33] correct rouding calculation, correct single meter_rate_charge pattern calculation --- .../api/v1/power_supply_plan_controller.rb | 15 +++++---------- ...remove_rounding_amount_method_from_provider.rb | 9 +++++++++ serverside_challenge_2/challenge/db/schema.rb | 3 +-- serverside_challenge_2/challenge/db/seeds.rb | 6 +++--- 4 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 serverside_challenge_2/challenge/db/migrate/20250726103216_remove_rounding_amount_method_from_provider.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index d0eebd2fc..cf6353713 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -23,7 +23,7 @@ def simulate_all result << { provider_name: plan.provider.provider_name, plan_name: plan.plan_name.to_s, - price: round_expense(basic_charge.first.price.to_f + meter_charge_price, plan) + price: (basic_charge.first.price.to_f + meter_charge_price).floor(0).to_i } end render json: result @@ -31,15 +31,6 @@ def simulate_all private - def round_expense(price, plan) - round_method = plan.provider.rounding_amount_method - if round_method == 'round' - price.round(0).to_i - elsif round_method == 'off' - price.floor(0).to_i - end - end - def meter_charge(plan, meter_rate) price = 0 plan.meter_rate_charges.each do |step| @@ -52,6 +43,10 @@ def calculate_if_step_applicable(step, meter_rate) max_rate = step.max_meter_rate min_rate = step.min_meter_rate price = step.price + + # 最小ステップ かつ 最大ステップ + return meter_rate * price if max_rate.nil? + # 使用量がステップ請求額に満たない場合0を返す return 0 if meter_rate < min_rate if max_rate.nil? && min_rate <= meter_rate diff --git a/serverside_challenge_2/challenge/db/migrate/20250726103216_remove_rounding_amount_method_from_provider.rb b/serverside_challenge_2/challenge/db/migrate/20250726103216_remove_rounding_amount_method_from_provider.rb new file mode 100644 index 000000000..d9d065ba2 --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250726103216_remove_rounding_amount_method_from_provider.rb @@ -0,0 +1,9 @@ +class RemoveRoundingAmountMethodFromProvider < ActiveRecord::Migration[7.0] + def up + remove_column :providers, :rounding_amount_method, :string + end + + def down + add_column :providers, :rounding_amount_method, :string + end +end \ No newline at end of file diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb index b694cc928..b3957b6e8 100644 --- a/serverside_challenge_2/challenge/db/schema.rb +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_07_23_134304) do +ActiveRecord::Schema[7.0].define(version: 2025_07_26_103216) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -43,7 +43,6 @@ create_table "providers", force: :cascade do |t| t.string "provider_name", null: false - t.string "rounding_amount_method", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index a13066f40..51180f067 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -10,9 +10,9 @@ Provider.create! ( [ - { provider_name: "東京電力エナジーパートナー", rounding_amount_method: "off" }, - { provider_name: "東京ガス", rounding_amount_method: "off" }, - { provider_name: "Looopでんき", rounding_amount_method: "round" } + { provider_name: "東京電力エナジーパートナー" }, + { provider_name: "東京ガス" }, + { provider_name: "Looopでんき" } ] ) From 5f7d63e16733efcd51bce33c04f59d95b565e070 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Sat, 26 Jul 2025 20:20:53 +0900 Subject: [PATCH 24/33] change column provider_name and plan_name to name --- .../api/v1/power_supply_plan_controller.rb | 4 ++-- ...26111141_rename_provider_name_column_to_name.rb | 5 +++++ ...50726111346_rename_plan__name_column_to_name.rb | 5 +++++ serverside_challenge_2/challenge/db/schema.rb | 6 +++--- serverside_challenge_2/challenge/db/seeds.rb | 14 +++++++------- 5 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 serverside_challenge_2/challenge/db/migrate/20250726111141_rename_provider_name_column_to_name.rb create mode 100644 serverside_challenge_2/challenge/db/migrate/20250726111346_rename_plan__name_column_to_name.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index cf6353713..b84698af9 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -21,8 +21,8 @@ def simulate_all next if meter_charge_price.negative? result << { - provider_name: plan.provider.provider_name, - plan_name: plan.plan_name.to_s, + provider_name: plan.provider.name, + plan_name: plan.name.to_s, price: (basic_charge.first.price.to_f + meter_charge_price).floor(0).to_i } end diff --git a/serverside_challenge_2/challenge/db/migrate/20250726111141_rename_provider_name_column_to_name.rb b/serverside_challenge_2/challenge/db/migrate/20250726111141_rename_provider_name_column_to_name.rb new file mode 100644 index 000000000..4f338026f --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250726111141_rename_provider_name_column_to_name.rb @@ -0,0 +1,5 @@ +class RenameProviderNameColumnToName < ActiveRecord::Migration[7.0] + def change + rename_column :providers, :provider_name, :name + end +end diff --git a/serverside_challenge_2/challenge/db/migrate/20250726111346_rename_plan__name_column_to_name.rb b/serverside_challenge_2/challenge/db/migrate/20250726111346_rename_plan__name_column_to_name.rb new file mode 100644 index 000000000..2bd08e0d5 --- /dev/null +++ b/serverside_challenge_2/challenge/db/migrate/20250726111346_rename_plan__name_column_to_name.rb @@ -0,0 +1,5 @@ +class RenamePlanNameColumnToName < ActiveRecord::Migration[7.0] + def change + rename_column :power_supply_plans, :plan_name, :name + end +end diff --git a/serverside_challenge_2/challenge/db/schema.rb b/serverside_challenge_2/challenge/db/schema.rb index b3957b6e8..ee18ba699 100644 --- a/serverside_challenge_2/challenge/db/schema.rb +++ b/serverside_challenge_2/challenge/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_07_26_103216) do +ActiveRecord::Schema[7.0].define(version: 2025_07_26_111346) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -35,14 +35,14 @@ create_table "power_supply_plans", force: :cascade do |t| t.bigint "provider_id" - t.string "plan_name", null: false + t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["provider_id"], name: "index_power_supply_plans_on_provider_id" end create_table "providers", force: :cascade do |t| - t.string "provider_name", null: false + t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index 51180f067..769571884 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -10,9 +10,9 @@ Provider.create! ( [ - { provider_name: "東京電力エナジーパートナー" }, - { provider_name: "東京ガス" }, - { provider_name: "Looopでんき" } + { name: "東京電力エナジーパートナー" }, + { name: "東京ガス" }, + { name: "Looopでんき" } ] ) @@ -21,14 +21,14 @@ provider = Provider.find(1) plans = provider.power_supply_plans.new( [ - {plan_name: "従量電灯B"}, - {plan_name: "スタンダードS"} + {name: "従量電灯B"}, + {name: "スタンダードS"} ] ) plans.each { |plan| plan.save } -provider = Provider.find(2).power_supply_plans.create!(plan_name: "ずっとも電気1") -provider = Provider.find(3).power_supply_plans.create!(plan_name: "おうちプラン") +provider = Provider.find(2).power_supply_plans.create!(name: "ずっとも電気1") +provider = Provider.find(3).power_supply_plans.create!(name: "おうちプラン") p '==================== basic_charge create ====================' From 517b890694e6a3c8e23b05589870e70199ed268d Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 28 Jul 2025 13:49:04 +0900 Subject: [PATCH 25/33] correct meter_rate calculation condition for single meter charge step paln --- .../app/controllers/api/v1/power_supply_plan_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index b84698af9..1e58be059 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -44,8 +44,9 @@ def calculate_if_step_applicable(step, meter_rate) min_rate = step.min_meter_rate price = step.price - # 最小ステップ かつ 最大ステップ - return meter_rate * price if max_rate.nil? + + # 最小ステップ かつ 最大ステップ(従量料金設定が1ステップのみのケース) + return meter_rate * price if min_rate.zero? && max_rate.nil? # 使用量がステップ請求額に満たない場合0を返す return 0 if meter_rate < min_rate From 519b5a69b6300d7ea9b07c1804003a98d2ab82d4 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Mon, 28 Jul 2025 13:51:05 +0900 Subject: [PATCH 26/33] rubocop code correction: db/seeds.rb --- serverside_challenge_2/challenge/db/seeds.rb | 139 +++++++++---------- 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/serverside_challenge_2/challenge/db/seeds.rb b/serverside_challenge_2/challenge/db/seeds.rb index 769571884..5b2bbf57e 100644 --- a/serverside_challenge_2/challenge/db/seeds.rb +++ b/serverside_challenge_2/challenge/db/seeds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file should contain all the record creation needed to seed the database with its default values. # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). # @@ -8,11 +10,11 @@ p '==================== provider create ====================' -Provider.create! ( +Provider.create!( [ - { name: "東京電力エナジーパートナー" }, - { name: "東京ガス" }, - { name: "Looopでんき" } + { name: '東京電力エナジーパートナー' }, + { name: '東京ガス' }, + { name: 'Looopでんき' } ] ) @@ -21,92 +23,83 @@ provider = Provider.find(1) plans = provider.power_supply_plans.new( [ - {name: "従量電灯B"}, - {name: "スタンダードS"} + { name: '従量電灯B' }, + { name: 'スタンダードS' } ] ) -plans.each { |plan| plan.save } +plans.each(&:save) -provider = Provider.find(2).power_supply_plans.create!(name: "ずっとも電気1") -provider = Provider.find(3).power_supply_plans.create!(name: "おうちプラン") +Provider.find(2).power_supply_plans.create!(name: 'ずっとも電気1') +Provider.find(3).power_supply_plans.create!(name: 'おうちプラン') p '==================== basic_charge create ====================' seed_data = [ - {index: 1, data: [ - {amp: 10, price: 286.00}, - {amp: 15, price: 429.00}, - {amp: 20, price: 572.00}, - {amp: 30, price: 858.00}, - {amp: 40, price: 1144.00}, - {amp: 50, price: 1430.00}, - {amp: 60, price: 1716.00} - ] - }, - {index: 2, data: [ - {amp: 10, price: 311.75}, - {amp: 15, price: 467.63}, - {amp: 20, price: 623.50}, - {amp: 30, price: 935.25}, - {amp: 40, price: 1247.00}, - {amp: 50, price: 1558.75}, - {amp: 60, price: 1870.50} - ] - }, - {index: 3, data: [ - {amp: 30, price: 858.00}, - {amp: 40, price: 1144.00}, - {amp: 50, price: 1430.00}, - {amp: 60, price: 1716.00} - ] - }, - {index: 4, data: [ - {amp: 10, price: 0.00}, - {amp: 15, price: 0.00}, - {amp: 20, price: 0.00}, - {amp: 30, price: 0.00}, - {amp: 40, price: 0.00}, - {amp: 50, price: 0.00}, - {amp: 60, price: 0.00} - ] - } + { index: 1, data: [ + { amp: 10, price: 286.00 }, + { amp: 15, price: 429.00 }, + { amp: 20, price: 572.00 }, + { amp: 30, price: 858.00 }, + { amp: 40, price: 1144.00 }, + { amp: 50, price: 1430.00 }, + { amp: 60, price: 1716.00 } + ] }, + { index: 2, data: [ + { amp: 10, price: 311.75 }, + { amp: 15, price: 467.63 }, + { amp: 20, price: 623.50 }, + { amp: 30, price: 935.25 }, + { amp: 40, price: 1247.00 }, + { amp: 50, price: 1558.75 }, + { amp: 60, price: 1870.50 } + ] }, + { index: 3, data: [ + { amp: 30, price: 858.00 }, + { amp: 40, price: 1144.00 }, + { amp: 50, price: 1430.00 }, + { amp: 60, price: 1716.00 } + ] }, + { index: 4, data: [ + { amp: 10, price: 0.00 }, + { amp: 15, price: 0.00 }, + { amp: 20, price: 0.00 }, + { amp: 30, price: 0.00 }, + { amp: 40, price: 0.00 }, + { amp: 50, price: 0.00 }, + { amp: 60, price: 0.00 } + ] } ] seed_data.each do |d| plan = PowerSupplyPlan.find(d[:index]) - basic_charges = plan.basic_charges.new( d[:data]) - basic_charges.each { |charge| charge.save } + basic_charges = plan.basic_charges.new(d[:data]) + basic_charges.each(&:save) end p '==================== meter_ratecharge create ====================' seed_data = [ - {index: 1, data: [ - {min_meter_rate: 0, max_meter_rate: 120, price: 19.88}, - {min_meter_rate: 121, max_meter_rate: 300, price: 26.48}, - {min_meter_rate: 301, max_meter_rate: nil, price: 30.57} - ] - }, - {index: 2, data: [ - {min_meter_rate: 0, max_meter_rate: 120, price: 29.80}, - {min_meter_rate: 121, max_meter_rate: 300, price: 36.40}, - {min_meter_rate: 301, max_meter_rate: nil, price: 40.49} - ] - }, - {index: 3, data: [ - {min_meter_rate: 0, max_meter_rate: 140, price: 23.67}, - {min_meter_rate: 141, max_meter_rate: 350, price: 23.88}, - {min_meter_rate: 351, max_meter_rate: nil, price: 26.41} - ] - }, - {index: 4, data: [ - {min_meter_rate: 0, max_meter_rate: nil, price: 28.80} - ] - } + { index: 1, data: [ + { min_meter_rate: 0, max_meter_rate: 120, price: 19.88 }, + { min_meter_rate: 121, max_meter_rate: 300, price: 26.48 }, + { min_meter_rate: 301, max_meter_rate: nil, price: 30.57 } + ] }, + { index: 2, data: [ + { min_meter_rate: 0, max_meter_rate: 120, price: 29.80 }, + { min_meter_rate: 121, max_meter_rate: 300, price: 36.40 }, + { min_meter_rate: 301, max_meter_rate: nil, price: 40.49 } + ] }, + { index: 3, data: [ + { min_meter_rate: 0, max_meter_rate: 140, price: 23.67 }, + { min_meter_rate: 141, max_meter_rate: 350, price: 23.88 }, + { min_meter_rate: 351, max_meter_rate: nil, price: 26.41 } + ] }, + { index: 4, data: [ + { min_meter_rate: 0, max_meter_rate: nil, price: 28.80 } + ] } ] seed_data.each do |d| plan = PowerSupplyPlan.find(d[:index]) - basic_charges = plan.meter_rate_charges.new( d[:data]) - basic_charges.each { |charge| charge.save } + basic_charges = plan.meter_rate_charges.new(d[:data]) + basic_charges.each(&:save) end - From db2b5d6b9620b75aca40622dcfd178ba219e06e1 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Wed, 30 Jul 2025 22:37:48 +0900 Subject: [PATCH 27/33] add service class, spread processing respoisibiities from controller to appropriate models --- .../api/v1/power_supply_plan_controller.rb | 111 +----------------- .../challenge/app/models/meter_rate_charge.rb | 22 ++++ .../challenge/app/models/power_supply_plan.rb | 18 +++ .../services/power_supply_plan_simulator.rb | 60 ++++++++++ 4 files changed, 103 insertions(+), 108 deletions(-) create mode 100644 serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb diff --git a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb index 1e58be059..294345c2c 100644 --- a/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb +++ b/serverside_challenge_2/challenge/app/controllers/api/v1/power_supply_plan_controller.rb @@ -4,118 +4,13 @@ module Api module V1 class PowerSupplyPlanController < ApplicationController def simulate_all - amp = params[:amp] - meter_rate = params[:meter_rate] - bad_request_result = build_200_message(amp, meter_rate) - return render json: bad_request_result, status: 400 if bad_request_result.present? + simulator = PowerSupplyPlanSimulator.new(params[:amp], params[:meter_rate]) + result = simulator.call - amp = amp.to_i - meter_rate = meter_rate.to_i - plans = PowerSupplyPlan.all - result = [] - plans.each do |plan| - basic_charge = plan.basic_charges.where(amp: amp) - next if basic_charge.blank? + return render json: [result] if result.is_a?(Hash) && result.key?(:error) - meter_charge_price = meter_charge(plan, meter_rate) - next if meter_charge_price.negative? - - result << { - provider_name: plan.provider.name, - plan_name: plan.name.to_s, - price: (basic_charge.first.price.to_f + meter_charge_price).floor(0).to_i - } - end render json: result end - - private - - def meter_charge(plan, meter_rate) - price = 0 - plan.meter_rate_charges.each do |step| - price += calculate_if_step_applicable(step, meter_rate) - end - price - end - - def calculate_if_step_applicable(step, meter_rate) - max_rate = step.max_meter_rate - min_rate = step.min_meter_rate - price = step.price - - - # 最小ステップ かつ 最大ステップ(従量料金設定が1ステップのみのケース) - return meter_rate * price if min_rate.zero? && max_rate.nil? - - # 使用量がステップ請求額に満たない場合0を返す - return 0 if meter_rate < min_rate - if max_rate.nil? && min_rate <= meter_rate - # 最大料金ステップの計算 - return (meter_rate - min_rate + 1) * price - end - - if min_rate.zero? - # 最小料金ステップの計算 - if max_rate <= meter_rate - # 該当ステップの満額請求計算 - max_rate * price - else - # 該当ステップの使用量までを計算 - meter_rate * price - end - elsif (min_rate..max_rate).include?(meter_rate) - # 中間料金ステップの計算 - # 該当ステップの使用量までを計算 - (meter_rate - min_rate + 1) * price - else - # 該当ステップの満額請求計算 - (max_rate - min_rate + 1) * price - end - end - - def build_200_message(amp, meter_rate) - message = '' - if amp.blank? - message += "リクエストパラメータ 'amp' が不足しています。\n" - else - unless valid_amp?(amp) - message += "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。\n" - end - end - if meter_rate.blank? - message += "リクエストパラメータ 'meter_rate' が不足しています。\n" - else - unless numeric?(meter_rate) && meter_rate.to_i >= 0 - message += "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。\n" - end - end - return errro_result(message) if message.present? - - [] - end - - def numeric?(string) - Integer(string) - true - rescue ArgumentError, TypeError - false - end - - def valid_amp?(amp) - [10, 15, 20, 30, 40, 50, 60].include?(amp.to_i) - end - - def errro_result(result) - [ - { - error: { - code: 400, - message: result - } - } - ] - end end end end diff --git a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb index b1007e517..2b932e845 100644 --- a/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb +++ b/serverside_challenge_2/challenge/app/models/meter_rate_charge.rb @@ -2,4 +2,26 @@ class MeterRateCharge < ApplicationRecord belongs_to :power_supply_plan + + def calculate_charge(meter_rate) + min_rate = min_meter_rate + max_rate = max_meter_rate + step_price = price + return 0 if min_rate > meter_rate + + if max_rate.nil? + return step_price * meter_rate if min_rate.zero? + + return step_price * (meter_rate - min_rate + 1) + end + if max_rate.to_i < meter_rate.to_i + return step_price * (max_rate - min_rate) if min_rate.zero? + + step_price * (max_rate - min_rate + 1) + else + return step_price * (meter_rate - min_rate) if min_rate.zero? + + step_price * (meter_rate - min_rate + 1) + end + end end diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb index 0a51bb519..4bb31212f 100644 --- a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb @@ -4,4 +4,22 @@ class PowerSupplyPlan < ApplicationRecord belongs_to :provider has_many :meter_rate_charges has_many :basic_charges + + def calculate_total_amount(amp, meter_rate) + basic_charge_record = basic_charges.where(amp: amp) + return nil if basic_charge_record.blank? + + basic_charge_amount = basic_charge_record.first.price.to_f + meter_rate_charge_amount = calculate_meter_rate_charge(meter_rate) + basic_charge_amount + meter_rate_charge_amount + end + + def calculate_meter_rate_charge(meter_rate) + total_meter_rate_charges = 0 + + meter_rate_charges.each do |step| + total_meter_rate_charges += step.calculate_charge(meter_rate.to_i) + end + total_meter_rate_charges + end end diff --git a/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb b/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb new file mode 100644 index 000000000..1a88afdfb --- /dev/null +++ b/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb @@ -0,0 +1,60 @@ +class PowerSupplyPlanSimulator + VALID_AMPS = [10, 15, 20, 30, 40, 50, 60].freeze + + def initialize(amp, meter_rate) + @amp = amp + @meter_rate = meter_rate + @errors = [] + end + + def call + unless validate_params + return { + error: { + code: 400, + message: @errors.join('\n') + } + } + end + + result = [] + PowerSupplyPlan.all.each do |plan| + calculate_result = plan.calculate_total_amount(@amp, @meter_rate) + next unless calculate_result + + result << { + "provider_name": plan.provider.name, + "plan_name": plan.name, + "price": calculate_result + } + end + result + end + + def validate_params + if @amp.blank? + @errors << "リクエストパラメータ 'amp' が不足しています。" + else + @errors << "リクエストパラメータ 'amp' の値が不正です。'10 / 15 / 20 / 30 / 40 / 50 / 60' のいずれかの値を指定してください。" unless valid_amp?(@amp) + end + if @meter_rate.blank? + @errors << "リクエストパラメータ 'meter_rate' が不足しています。" + else + unless numeric?(@meter_rate) && @meter_rate.to_i >= 0 + @errors << "リクエストパラメータ 'meter_rate' の値が不正です。0以上の整数を指定してください。" + end + end + @errors.blank? + end + + def numeric?(string) + Integer(string) + true + rescue ArgumentError, TypeError + false + end + + def valid_amp?(amp) + VALID_AMPS.include?(amp.to_i) + end +end From e1890a72a8c1e0daaea1a65ac1ed01d53d0b1d20 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 31 Jul 2025 11:54:07 +0900 Subject: [PATCH 28/33] prevent n+1 --- .../challenge/app/services/power_supply_plan_simulator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb b/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb index 1a88afdfb..f990e9430 100644 --- a/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb +++ b/serverside_challenge_2/challenge/app/services/power_supply_plan_simulator.rb @@ -18,7 +18,7 @@ def call end result = [] - PowerSupplyPlan.all.each do |plan| + PowerSupplyPlan.includes(:provider, :meter_rate_charges, :basic_charges).each do |plan| calculate_result = plan.calculate_total_amount(@amp, @meter_rate) next unless calculate_result From a0d952f753bbf5c2f72c90517cb22ce61c12fb5a Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 31 Jul 2025 22:01:43 +0900 Subject: [PATCH 29/33] add rspec gem --- serverside_challenge_2/challenge/.rspec | 1 + serverside_challenge_2/challenge/Gemfile | 1 + serverside_challenge_2/challenge/Gemfile.lock | 19 ++++ .../challenge/spec/rails_helper.rb | 69 ++++++++++++++ .../challenge/spec/spec_helper.rb | 94 +++++++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 serverside_challenge_2/challenge/.rspec create mode 100644 serverside_challenge_2/challenge/spec/rails_helper.rb create mode 100644 serverside_challenge_2/challenge/spec/spec_helper.rb diff --git a/serverside_challenge_2/challenge/.rspec b/serverside_challenge_2/challenge/.rspec new file mode 100644 index 000000000..c99d2e739 --- /dev/null +++ b/serverside_challenge_2/challenge/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index fdcd4135e..129c9b447 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -42,6 +42,7 @@ gem 'rubocop-rails', '~> 2.32' group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri mingw x64_mingw ] + gem 'rspec-rails' end group :development do diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index 87150bfaf..7dcfb5903 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -78,6 +78,7 @@ GEM debug (1.9.1) irb (~> 1.10) reline (>= 0.3.8) + diff-lcs (1.6.2) erubi (1.12.0) globalid (1.2.1) activesupport (>= 6.1) @@ -166,6 +167,23 @@ GEM regexp_parser (2.10.0) reline (0.4.2) io-console (~> 0.5) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (7.1.1) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.4) rubocop (1.78.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) @@ -211,6 +229,7 @@ DEPENDENCIES pg (~> 1.1) puma (~> 5.0) rails (~> 7.0.8) + rspec-rails rubocop-rails (~> 2.32) tzinfo-data diff --git a/serverside_challenge_2/challenge/spec/rails_helper.rb b/serverside_challenge_2/challenge/spec/rails_helper.rb new file mode 100644 index 000000000..0d761946b --- /dev/null +++ b/serverside_challenge_2/challenge/spec/rails_helper.rb @@ -0,0 +1,69 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require_relative '../config/environment' +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file +# that will avoid rails generators crashing because migrations haven't been run yet +# return unless Rails.env.test? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + abort e.to_s.strip +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = Rails.root.join('spec/fixtures') + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # RSpec Rails uses metadata to mix in different behaviours to your tests, + # for example enabling you to call `get` and `post` in request specs. e.g.: + # + # RSpec.describe UsersController, type: :request do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://rspec.info/features/7-1/rspec-rails + # + # You can also this infer these behaviours automatically by location, e.g. + # /spec/models would pull in the same behaviour as `type: :model` but this + # behaviour is considered legacy and will be removed in a future version. + # + # To enable this behaviour uncomment the line below. + # config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") + # +end diff --git a/serverside_challenge_2/challenge/spec/spec_helper.rb b/serverside_challenge_2/challenge/spec/spec_helper.rb new file mode 100644 index 000000000..327b58ea1 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/spec_helper.rb @@ -0,0 +1,94 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 18d89ba1767f0e090701574a9bc7ff6651b588e8 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 31 Jul 2025 22:03:25 +0900 Subject: [PATCH 30/33] add spec --- .../spec/models/meter_rate_charge.rb | 41 ++++++++ .../spec/models/power_supply_plan.rb | 46 +++++++++ .../services/power_supply_plan_simulator.rb | 96 +++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 serverside_challenge_2/challenge/spec/models/meter_rate_charge.rb create mode 100644 serverside_challenge_2/challenge/spec/models/power_supply_plan.rb create mode 100644 serverside_challenge_2/challenge/spec/services/power_supply_plan_simulator.rb diff --git a/serverside_challenge_2/challenge/spec/models/meter_rate_charge.rb b/serverside_challenge_2/challenge/spec/models/meter_rate_charge.rb new file mode 100644 index 000000000..a5a346831 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/models/meter_rate_charge.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe PowerSupplyPlan, type: :model do + let!(:provider1) { Provider.create!(id: 1, name: '東京ガス') } + let!(:provider2) { Provider.create!(id: 2, name: 'Looopでんき') } + let!(:plan1) { PowerSupplyPlan.create!(id: 3, provider_id: 1, name: 'ずっとも電気1') } + let!(:plan2) { PowerSupplyPlan.create!(id: 4, provider_id: 2, name: 'おうちプラン') } + let!(:meter_rate_charge1) { MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 0, max_meter_rate: 140, price: 23.67) } + let!(:meter_rate_charge2) { MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 141, max_meter_rate: 350, price: 23.88) } + let!(:meter_rate_charge3) { MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 351, max_meter_rate: nil, price: 26.41) } + let!(:meter_rate_charge4) { MeterRateCharge.create!(power_supply_plan_id: 4, min_meter_rate: 0, max_meter_rate: nil, price: 28.8) } + + describe '#calculate_charge' do + context 'calculate on 3 steps plan' do + it 'calculate result with 1st step' do + expect(meter_rate_charge1.calculate_charge(120).to_f).to eq 2840.4 + expect(meter_rate_charge1.calculate_charge(140).to_f).to eq 3313.8 + expect(meter_rate_charge1.calculate_charge(150).to_f).to eq 3313.8 + end + + it 'calculate result with middle step' do + expect(meter_rate_charge2.calculate_charge(150).to_f).to eq 238.8 + expect(meter_rate_charge2.calculate_charge(350).to_f).to eq 5014.8 + expect(meter_rate_charge2.calculate_charge(400).to_f).to eq 5014.8 + end + + it 'calculate result with highest step' do + expect(meter_rate_charge3.calculate_charge(351).to_f).to eq 26.41 + expect(meter_rate_charge3.calculate_charge(400).to_f).to eq 1320.5 + end + end + + context 'calculate on single step plan' do + it 'calculate result with single step plan' do + expect(meter_rate_charge4.calculate_charge(100).to_f).to eq 2880 + end + end + end +end diff --git a/serverside_challenge_2/challenge/spec/models/power_supply_plan.rb b/serverside_challenge_2/challenge/spec/models/power_supply_plan.rb new file mode 100644 index 000000000..331e86c5c --- /dev/null +++ b/serverside_challenge_2/challenge/spec/models/power_supply_plan.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe PowerSupplyPlan, type: :model do + let!(:provider) { Provider.create!(id: 1, name: '東京ガス') } + let!(:plan) { PowerSupplyPlan.create!(id: 3, provider_id: 1, name: 'ずっとも電気1') } + let!(:basic_charge1) { BasicCharge.create!(power_supply_plan_id: 3, amp: 30, price: 858.0) } + let!(:basic_charge2) { BasicCharge.create!(power_supply_plan_id: 3, amp: 40, price: 1144.0) } + let!(:basic_charge3) { BasicCharge.create!(power_supply_plan_id: 3, amp: 50, price: 1430.0) } + let!(:basic_charge4) { BasicCharge.create!(power_supply_plan_id: 3, amp: 60, price: 1716.0) } + let!(:meter_rate_charge1) do + MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 0, max_meter_rate: 140, price: 23.67) + end + let!(:meter_rate_charge2) do + MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 141, max_meter_rate: 350, price: 23.88) + end + let!(:meter_rate_charge3) do + MeterRateCharge.create!(power_supply_plan_id: 3, min_meter_rate: 351, max_meter_rate: nil, price: 26.41) + end + let(:amp) { 10 } + let(:meter_rate) { 120 } + describe '#calculate_total_amount' do + context 'when no meter rate charge available ' do + it 'returns nil' do + expect(plan.calculate_total_amount(amp, meter_rate)).to be nil + end + end + + context 'when meter rate charge available' do + it 'calculate with designated basic charge' do + allow(plan).to receive(:calculate_meter_rate_charge).with(meter_rate).and_return(1000.00) + expect(plan.calculate_total_amount(30, meter_rate).to_f).to eq 1858.00 + end + end + end + + describe '#calculate_meter_rate_charge' do + it 'returns calculation result with 1st step only' do + expect(plan.calculate_meter_rate_charge(meter_rate).to_f).to eq 2840.40 + end + it 'returns calculation result with 1st and 2nd step' do + expect(plan.calculate_meter_rate_charge(150).to_f).to eq 3552.6 + end + end +end diff --git a/serverside_challenge_2/challenge/spec/services/power_supply_plan_simulator.rb b/serverside_challenge_2/challenge/spec/services/power_supply_plan_simulator.rb new file mode 100644 index 000000000..fee48dd72 --- /dev/null +++ b/serverside_challenge_2/challenge/spec/services/power_supply_plan_simulator.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'PowerSupplyPlanSimulator' do + context 'when input parameter is valid' do + let(:service) { PowerSupplyPlanSimulator.new(amp, meter_rate) } + let(:result) { service.call } + let(:amp) { 10 } + let(:meter_rate) { 120 } + let(:provider1) { double(Provider, name: '東京電力エナジーパートナー') } + let(:provider2) { double(Provider, name: '東京ガス') } + let(:plan1) { double(PowerSupplyPlan, name: '従量電灯B', provider: provider1) } + let(:plan2) { double(PowerSupplyPlan, name: 'ずっとも電気1', provider: provider2) } + + it 'returns all valid array' do + allow(PowerSupplyPlan).to receive(:includes).and_return([plan1, plan2]) + allow(plan1).to receive(:calculate_total_amount).with(amp, meter_rate).and_return 4101.6 + allow(plan2).to receive(:calculate_total_amount).with(amp, meter_rate).and_return 4556.4 + + expect(result.length).to eq 2 + expect(result).to include({ "provider_name": '東京電力エナジーパートナー', plan_name: '従量電灯B', price: 4101.6 }) + expect(result).to include({ "provider_name": '東京ガス', plan_name: 'ずっとも電気1', price: 4556.4 }) + end + + it 'returns result with basic_charge availble plans' do + allow(PowerSupplyPlan).to receive(:includes).and_return([plan1, plan2]) + allow(plan1).to receive(:calculate_total_amount).with(amp, meter_rate).and_return 4101.6 + allow(plan2).to receive(:calculate_total_amount).with(amp, meter_rate).and_return nil + + expect(result.length).to eq 1 + expect(result).to include({ "provider_name": '東京電力エナジーパートナー', plan_name: '従量電灯B', price: 4101.6 }) + expect(result).not_to include({ "provider_name": '東京ガス', plan_name: 'ずっとも電気1', price: 4556.4 }) + end + end + + context 'when input parameter is invalid' do + let(:service) { PowerSupplyPlanSimulator.new(amp, meter_rate) } + let(:result) { service.call } + + context 'invalid amp' do + let(:amp) { -10 } + let(:meter_rate) { 120 } + + it 'validate_params to be false and return error hash ' do + expect(result.is_a?(Hash)).to be_truthy + end + + it 'returns amp invalid error' do + expect(result.is_a?(Hash)).to be_truthy + expect(result[:error][:message]).to start_with "リクエストパラメータ 'amp' の値が不正です。" + end + end + + context 'invalid meter_rate' do + let(:amp) { 10 } + let(:meter_rate) { -120 } + + it 'validate_params to be false' do + expect(result.is_a?(Hash)).to be_truthy + end + + it 'return meter_rate invalid error' do + expect(result[:error][:message]).to start_with "リクエストパラメータ 'meter_rate' の値が不正です。" + end + end + + context 'amp is missing' do + let(:amp) { nil } + let(:meter_rate) { 120 } + + it 'validate_params to be false' do + expect(result.is_a?(Hash)).to be_truthy + end + + it 'return amp missing error' do + expect(result.is_a?(Hash)).to be_truthy + expect(result[:error][:message]).to eq "リクエストパラメータ 'amp' が不足しています。" + end + end + + context 'meter_rate is missing' do + let(:amp) { 10 } + let(:meter_rate) { nil } + + it 'validate_params to be false' do + expect(result.is_a?(Hash)).to be_truthy + end + + it 'return amp missing error' do + expect(result.is_a?(Hash)).to be_truthy + expect(result[:error][:message]).to eq "リクエストパラメータ 'meter_rate' が不足しています。" + end + end + end +end From ce69d1994d244f89b5c37c970a32e603c491ee66 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 31 Jul 2025 22:14:04 +0900 Subject: [PATCH 31/33] change api method post to get --- serverside_challenge_2/challenge/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/config/routes.rb b/serverside_challenge_2/challenge/config/routes.rb index 001968f1d..e73a37fdf 100644 --- a/serverside_challenge_2/challenge/config/routes.rb +++ b/serverside_challenge_2/challenge/config/routes.rb @@ -5,7 +5,7 @@ # root "articles#index" namespace :api do namespace :v1 do - post "simulate_all_plan", to: "power_supply_plan#simulate_all" + get "simulate_all_plan", to: "power_supply_plan#simulate_all" end end end From 344ac7d1f9e4b391939dad1849e5b622a2916691 Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Thu, 31 Jul 2025 22:48:19 +0900 Subject: [PATCH 32/33] truncate total amount --- .../challenge/app/models/power_supply_plan.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb index 4bb31212f..5bbb3f71b 100644 --- a/serverside_challenge_2/challenge/app/models/power_supply_plan.rb +++ b/serverside_challenge_2/challenge/app/models/power_supply_plan.rb @@ -11,7 +11,7 @@ def calculate_total_amount(amp, meter_rate) basic_charge_amount = basic_charge_record.first.price.to_f meter_rate_charge_amount = calculate_meter_rate_charge(meter_rate) - basic_charge_amount + meter_rate_charge_amount + (basic_charge_amount + meter_rate_charge_amount).floor(0).to_i end def calculate_meter_rate_charge(meter_rate) From f437eb7f80d865a8702ad38db55073a2de83658a Mon Sep 17 00:00:00 2001 From: m-nakamura Date: Fri, 1 Aug 2025 01:20:49 +0900 Subject: [PATCH 33/33] remove active_hash --- serverside_challenge_2/challenge/Gemfile | 1 - serverside_challenge_2/challenge/Gemfile.lock | 3 --- 2 files changed, 4 deletions(-) diff --git a/serverside_challenge_2/challenge/Gemfile b/serverside_challenge_2/challenge/Gemfile index 129c9b447..a12bf9c7d 100644 --- a/serverside_challenge_2/challenge/Gemfile +++ b/serverside_challenge_2/challenge/Gemfile @@ -36,7 +36,6 @@ gem "bootsnap", require: false # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem "rack-cors" -gem "active_hash" gem 'rubocop-rails', '~> 2.32' group :development, :test do diff --git a/serverside_challenge_2/challenge/Gemfile.lock b/serverside_challenge_2/challenge/Gemfile.lock index 7dcfb5903..d6148432f 100644 --- a/serverside_challenge_2/challenge/Gemfile.lock +++ b/serverside_challenge_2/challenge/Gemfile.lock @@ -46,8 +46,6 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - active_hash (3.3.1) - activesupport (>= 5.0.0) activejob (7.0.8) activesupport (= 7.0.8) globalid (>= 0.3.6) @@ -223,7 +221,6 @@ PLATFORMS x86_64-linux DEPENDENCIES - active_hash bootsnap debug pg (~> 1.1)