diff --git a/app/controllers/api/v3/inputs_controller.rb b/app/controllers/api/v3/inputs_controller.rb index fb7b81ac4..cea2d394d 100644 --- a/app/controllers/api/v3/inputs_controller.rb +++ b/app/controllers/api/v3/inputs_controller.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require 'csv' module Api module V3 @@ -10,18 +11,19 @@ class InputsController < ::Api::V3::BaseController # GET /api/v3/inputs # GET /api/v3/scenarios/:scenario_id/inputs + # GET /api/v3/scenarios/:scenario_id/inputs.csv # - # Returns the details for all available inputs. If the scenario_id isn't - # passed then the action will use the latest scenario. - # + # Returns input details in JSON or CSV format. Uses the latest scenario if + # scenario_id is not provided. + def index extras = ActiveModel::Type::Boolean.new.cast(params[:include_extras]) + inputs = serialized_inputs(extras) - render json: InputSerializer.collection( - Input.all, - @scenario, - **serializer_args(extra_attributes: extras) - ) + respond_to do |format| + format.json { render json: inputs } + format.csv { send_csv_data(inputs) } + end end # GET /api/v3/inputs/:id @@ -77,6 +79,53 @@ def serializer_args(extra_attributes:) extra_attributes: } end + + def serialized_inputs(extras) + InputSerializer.collection( + Input.all, + @scenario, + **serializer_args(extra_attributes: extras) + ) + end + + def send_csv_data(inputs) + csv_data = generate_csv(inputs) + send_data csv_data, filename: "inputs_#{@scenario.id}.csv" + end + + def generate_csv(inputs) + CSV.generate(headers: true) do |csv| + csv << csv_headers + cached_values = Input.cache(@scenario.parent) + user_values = @scenario.user_values + + inputs.each do |key, input| + add_csv_row(csv, key, input, cached_values, user_values) + end + end + end + + def csv_headers + ["Key", "Min", "Max", "Default", "User Value", "Unit", "Share Group"] + end + + def add_csv_row(csv, key, input, cached_values, user_values) + input_data = input.instance_variable_get(:@input) + return if input_data.nil? + + values = cached_values.read(@scenario.parent, input_data) + default_value = input.instance_variable_get(:@default_values_from).call(values) + + csv << [ + key, + input_data.min_value, + input_data.max_value, + default_value, + user_values[input_data.key] || "", + input_data.unit, + input_data.share_group + ] + end end end end diff --git a/config/routes.rb b/config/routes.rb index 77039dcb3..7ef9004a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -85,7 +85,7 @@ get 'converters', to: redirect('/api/v3/scenarios/%{scenario_id}/nodes') get 'converters/:id', to: redirect('/api/v3/scenarios/%{scenario_id}/nodes/%{id}') - resources :inputs, :only => [:index, :show] + resources :inputs, :only => [:index, :show], defaults: { format: :json} resource :version, :only => [:create, :show, :update], controller: 'scenario_version_tags' diff --git a/spec/controllers/api/v3/inputs_controller_spec.rb b/spec/controllers/api/v3/inputs_controller_spec.rb index 96ba621e1..4d383e2ae 100644 --- a/spec/controllers/api/v3/inputs_controller_spec.rb +++ b/spec/controllers/api/v3/inputs_controller_spec.rb @@ -7,7 +7,8 @@ FactoryBot.build(:input, { min_value: 5, max_value: 15, - start_value: 10 + start_value: 10, + unit: 'kWh' }) end @@ -15,19 +16,20 @@ FactoryBot.build(:input, { start_value_gql: 'present:2 * 4', min_value_gql: 'present:2 * 2', - max_value_gql: 'present:2 * 8' + max_value_gql: 'present:2 * 8', + unit: 'MW' }) end before do NastyCache.instance.expire! - allow(Input).to receive(:all).and_return([ static_input, gql_input ]) + allow(Input).to receive(:all).and_return([static_input, gql_input]) end # -------------------------------------------------------------------------- - describe 'GET /api/v3/scenarios/:scenario_id/inputs' do - let(:json) { JSON.parse(get(:index, params: { scenario_id: scenario.id }).body) } + describe 'GET /api/v3/scenarios/:scenario_id/inputs (JSON format)' do + let(:json) { JSON.parse(get(:index, params: { scenario_id: scenario.id, format: :json }).body) } it 'is successful' do json @@ -174,7 +176,8 @@ end let(:json) do - JSON.parse(get(:index, params: { scenario_id: scenario.id, defaults: 'parent' }).body) + response = get(:index, params: { scenario_id: scenario.id, defaults: 'parent', format: :json }) + JSON.parse(response.body) end it 'has a "default" attribute for each input based on the parent' do @@ -192,7 +195,7 @@ end let(:json) do - JSON.parse(get(:index, params: { scenario_id: scenario.id, defaults: 'original' }).body) + JSON.parse(get(:index, params: { scenario_id: scenario.id, defaults: 'original', format: :json }).body) end it 'has a "default" attribute for each input based on the dataset' do @@ -211,7 +214,7 @@ gql_input.key => gql_input }) - get(:show, params: { scenario_id: scenario.id, id: static_input.key }) + get(:show, params: { scenario_id: scenario.id, id: static_input.key, format: :json }) JSON.parse(response.body) end @@ -303,5 +306,4 @@ expect(json.any? { |v| v['code'] == gql_input.key }).to be_falsey end end # GET /api/v3/scenarios/:scenario_id/inputs - end