Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions serverside_challenge_2/challenge/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@

# Ignore master key for decrypting credentials and more.
/config/master.key

# Ignore Editor's filepath
.vscode
.idea

.tool-versions
75 changes: 62 additions & 13 deletions serverside_challenge_2/challenge/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,73 @@
# README
# 電気料金出力API
## 概要
与えられたリクエスト値を元に、一致する電力会社/プランの料金を返却するAPIです。

This README would normally document whatever steps are necessary to get the
application up and running.
## API仕様
### エンドポイント
```
GET electricity_charges/calculate
```

Things you may want to cover:
### リクエスト値

* Ruby version
| パラメータ名 | 説明 | データ型 | パラメータ制限 | 備考 |
|--------|---------|------|----------------------------|---------|
| ampere | 契約アンペア数 | int | 10/15/20/30/40/50/60 のいずれか | 単位: A |
| usage | 電力使用量 | int | 0以上の整数 | 単位: kWh |

* System dependencies
### レスポンス
| パラメータ名 | 説明 | データ型 | 備考 |
|---------------|-------|--------|----|
| provider_name | 電力会社名 | string | |
| plan_name | プラン名 | string | |
| price | 値段 | float | |

* Configuration
### レスポンスのサンプル
#### 一致するプランがある場合

* Database creation
```json lines
// GET http://localhost:3000/electricity_charges/calculate?ampere=30&usage=10

* Database initialization
[
{
"provider_name": "東京電力エナジーパートナー",
"plan_name": "従量電灯B",
"price": 1056.8
},
{
"provider_name": "東京電力エナジーパートナー",
"plan_name": "スタンダードS",
"price": 1233.25
},
{
"provider_name": "東京ガス",
"plan_name": "ずっとも電気1",
"price": 1094.7
},
{
"provider_name": "Looopでんき",
"plan_name": "おうちプラン",
"price": 288.0
}
]
```

* How to run the test suite
#### 一致するプランがない場合

* Services (job queues, cache servers, search engines, etc.)
```json
[
{
"message": "ampereに一致するプランが登録されていません"
}
]
```

* Deployment instructions

* ...
#### パラメータが不正な場合

```json

{
"error": "ampereの値が正しくありません"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class ElectricityCharges::CalculateController < ApplicationController
include ElectricChargesHelper
def calc
error, status = validate_params(params)
if error
return render json: error, status: status
end

calc_result, calc_status = import_price_from_yaml(params[:ampere].to_i, params[:usage].to_i)

render json: calc_result, status: calc_status
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
module ElectricChargesHelper
VALID_CONTRACT_AMPERES = %w(10 15 20 30 40 50 60).freeze
VALID_USAGE_REGEX = /\A\d+\z/.freeze

# リクエストパラメータのバリデーション
def validate_params(request_params)
# パラメータが不足しているとき
if request_params[:ampere].nil? || request_params[:usage].nil?
return { error: I18n.t('errors.empty_parameter') }, :bad_request
end

# ampereは特定値である必要がある
unless VALID_CONTRACT_AMPERES.include?(request_params[:ampere])
return { error: I18n.t('errors.invalid_contract_ampere') }, :bad_request
end

# usageは0以上の整数である必要がある
usage = request_params[:usage].to_i
unless VALID_USAGE_REGEX.match?(request_params[:usage])
return { error: I18n.t('errors.invalid_usage') }, :bad_request
end
if usage < 0
return { error: I18n.t('errors.invalid_usage') }, :bad_request
end

nil
end

# yamlからパラメータ読み込み
def import_price_from_yaml(ampere, usage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import_price_from_yaml関数が、ymlからデータを読み込む以上の処理をしています。

関数内の処理を別関数に切り出すなどした方が保守性・可読性は上がると思いましたが、いかがでしょうか。

results = []
electricity_charges_data = YAML.load_file("config/electricity_charges.yml")
# 基本料金
electricity_charges_data["basic_price"].each do |provider|
provider["plans"].each do |plan|
if plan["amperes"].has_key?(ampere)
results << {
"provider_name": provider["provider_name"],
"plan_name": plan["plan"],
"price": plan["amperes"][ampere]
}
end
end
end

# ampereのパラメータに紐づくデータがない場合はその旨を返す
if results.empty?
results << { message: I18n.t('message.empty_result_by_ampere') }
return results, 404
end

# 従量料金
results.each do |result|
provider_usage_price_data = electricity_charges_data["usage_price"].select{|provider| provider["provider_name"] == result[:provider_name]}
provider_usage_price_data.each do |data|
plans = data["plans"].select {|plan| plan["plan"] == result[:plan_name]}
plans.each do |plan|
thresholds = plan["thresholds"]
keys = thresholds.keys.map(&:to_i).sort.reverse
keys.each do |key|
if usage >= key
unit_price_kwh = thresholds[key]
usage_price = (unit_price_kwh * usage).round(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1000kwhの使用量でAPI実行した場合に、東京電力・従量電灯Bの場合に、電力量料金が 1000kwh * 30.57 になっており、段階的に「使用量 * 単価」の計算がされていないようです。

https://www.tepco.co.jp/ep/private/plan2/chargelist04.html#sec03

result[:price] += usage_price
break
end
end
end
end
end

[results, 200]
end
end
65 changes: 65 additions & 0 deletions serverside_challenge_2/challenge/config/electricity_charges.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
basic_price:
- provider_name: 東京電力エナジーパートナー
plans:
- plan: 従量電灯B
amperes:
10: 286.00
15: 429.00
20: 572.00
30: 858.00
40: 1144.00
50: 1430.00
60: 1716.00
- plan: スタンダードS
amperes:
10: 311.75
15: 467.63
20: 623.50
30: 935.25
40: 1247.00
50: 1558.75
60: 1870.50
- provider_name: 東京ガス
plans:
- plan: ずっとも電気1
amperes:
30: 858.00
40: 1144.00
50: 1430.00
60: 1716.00
- provider_name: Looopでんき
plans:
- plan: おうちプラン
amperes:
10: 0.00
15: 0.00
20: 0.00
30: 0.00
40: 0.00
50: 0.00
60: 0.00
usage_price:
- provider_name: 東京電力エナジーパートナー
plans:
- plan: 従量電灯B
thresholds:
0: 19.88
121: 26.48
301: 30.57
- plan: スタンダードS
thresholds:
0: 29.80
121: 36.40
301: 40.49
- provider_name: 東京ガス
plans:
- plan: ずっとも電気1
thresholds:
0: 23.67
141: 23.88
351: 26.41
- provider_name: Looopでんき
plans:
- plan: おうちプラン
thresholds:
0: 28.8
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@
# available at https://guides.rubyonrails.org/i18n.html.

en:
hello: "Hello world"
errors:
empty_parameter: 'リクエストパラメータにampereとusageを設定してください'
invalid_parameter_type: 'ampereとusageはいずれも数値を設定してください'
invalid_contract_ampere: 'ampereの値が正しくありません'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どのような値を設定すれば正しいのか判別できるエラーメッセージだと親切かと思いました。

invalid_usage: 'usageは0以上の整数を設定してください'
message:
empty_result_by_ampere: 'ampereに一致するプランが登録されていません'

7 changes: 3 additions & 4 deletions serverside_challenge_2/challenge/config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

# Defines the root path route ("/")
# root "articles#index"
namespace :electricity_charges do
get 'calculate', to: 'calculate#calc'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require "test_helper"

class ElectricityCharges::CalculateControllerTest < ActionDispatch::IntegrationTest
test '正常系: 契約アンペア数 10A, 使用量 0kWh' do
expected = [
{ provider_name: '東京電力エナジーパートナー', plan_name: '従量電灯B', price: 286.0 },
{ provider_name: '東京電力エナジーパートナー', plan_name: 'スタンダードS', price: 311.75 },
{ provider_name: 'Looopでんき', plan_name: 'おうちプラン', price: 0.0 },
]
get electricity_charges_calculate_url, params: { ampere: 10, usage: 0 }
assert_response :success
assert_equal(expected.to_json, response.body)
end

test '正常系: 契約アンペア数 30A, 使用量 100kWh' do
expected = [
{ provider_name: '東京電力エナジーパートナー', plan_name: '従量電灯B', price: 2846.0 },
{ provider_name: '東京電力エナジーパートナー', plan_name: 'スタンダードS', price: 3915.25 },
{ provider_name: '東京ガス', plan_name: 'ずっとも電気1', price: 3225.0 },
{ provider_name: 'Looopでんき', plan_name: 'おうちプラン', price: 2880.0 },
]
get electricity_charges_calculate_url, params: { ampere: 30, usage: 100 }
assert_response :success
assert_equal(expected.to_json, response.body)
end

test '正常系: 契約アンペア数 60A, 使用量 1000kWh' do
expected = [
{ provider_name: '東京電力エナジーパートナー', plan_name: '従量電灯B', price: 32286.0 },
{ provider_name: '東京電力エナジーパートナー', plan_name: 'スタンダードS', price: 42360.5 },
{ provider_name: '東京ガス', plan_name: 'ずっとも電気1', price: 28126.0 },
{ provider_name: 'Looopでんき', plan_name: 'おうちプラン', price: 28800.0 },
]
get electricity_charges_calculate_url, params: { ampere: 60, usage: 1000 }
assert_response :success
assert_equal(expected.to_json, response.body)
end

test '異常系: 契約アンペア数 0A' do
get electricity_charges_calculate_url, params: { ampere: 0, usage: 100 }
assert_response :bad_request
assert_equal({ 'error' => 'ampereの値が正しくありません' }.to_json, response.body)
end

test '異常系: 契約アンペア数 70A' do
get electricity_charges_calculate_url, params: { ampere: 70, usage: 100 }
assert_response :bad_request
assert_equal({ 'error' => 'ampereの値が正しくありません' }.to_json, response.body)
end

test '異常系: 使用量 -1kWh' do
get electricity_charges_calculate_url, params: { ampere: 30, usage: -1 }
assert_response :bad_request
assert_equal({ 'error' => 'usageは0以上の整数を設定してください' }.to_json, response.body)
end

test '異常系: 使用量 1.5kWh (型が不正)' do
get electricity_charges_calculate_url, params: { ampere: 30, usage: 1.5 }
assert_response :bad_request
assert_equal({ 'error' => 'usageは0以上の整数を設定してください' }.to_json, response.body)
end

test '異常系: 契約アンペア数 "abc" (型が不正)' do
get electricity_charges_calculate_url, params: { ampere: "abc", usage: 100 }
assert_response :bad_request
assert_equal({ 'error' => 'ampereの値が正しくありません' }.to_json, response.body)
end

test '異常系: 使用量 "abc" (型が不正)' do
get electricity_charges_calculate_url, params: { ampere: 30, usage: "abc" }
assert_response :bad_request
assert_equal({"error"=>"usageは0以上の整数を設定してください"}.to_json, response.body)
end

test '異常系: パラメータが空' do
get electricity_charges_calculate_url, params: {}
assert_response :bad_request
assert_equal({"error"=>"リクエストパラメータにampereとusageを設定してください"}.to_json, response.body)
end
end
Loading