diff --git a/app/components/sdg/related_list_selector_component.rb b/app/components/sdg/related_list_selector_component.rb index b183765ded5f..170df796b32a 100644 --- a/app/components/sdg/related_list_selector_component.rb +++ b/app/components/sdg/related_list_selector_component.rb @@ -15,7 +15,10 @@ def sdg_related_suggestions def goals_and_targets goals.map do |goal| - [goal, *goal.targets.sort] + global_targets = goal.targets + local_targets = SDG::LocalTarget.where(target: [global_targets]) + targets = global_targets + local_targets + [goal, targets.sort] end.flatten end diff --git a/app/controllers/sdg_management/local_targets_controller.rb b/app/controllers/sdg_management/local_targets_controller.rb index 0d03a743f506..798866ffd512 100644 --- a/app/controllers/sdg_management/local_targets_controller.rb +++ b/app/controllers/sdg_management/local_targets_controller.rb @@ -11,6 +11,7 @@ def new end def create + add_related_goal if @local_target.save redirect_to sdg_management_local_targets_path, notice: t("sdg_management.local_targets.create.notice") else @@ -40,4 +41,8 @@ def local_target_params translations_attributes = translation_params(::SDG::LocalTarget) params.require(:sdg_local_target).permit(:code, :target_id, translations_attributes) end + + def add_related_goal + @local_target.goal = @local_target.target.goal + end end diff --git a/app/models/concerns/sdg/relatable.rb b/app/models/concerns/sdg/relatable.rb index 636feca73958..1b3919ea3bee 100644 --- a/app/models/concerns/sdg/relatable.rb +++ b/app/models/concerns/sdg/relatable.rb @@ -75,18 +75,24 @@ def sdg_target_list def sdg_related_list sdg_goals.order(:code).map do |goal| - [goal, sdg_global_targets.where(goal: goal).sort] + global_targets = sdg_global_targets.where(goal: goal) + local_targets = sdg_local_targets.where(goal: goal) + targets_related_with_goal = global_targets + local_targets + [goal, targets_related_with_goal.sort] end.flatten.map(&:code).join(", ") end def sdg_related_list=(codes) target_codes, goal_codes = codes.tr(" ", "").split(",").partition { |code| code.include?(".") } - targets = target_codes.map { |code| SDG::Target[code] } + local_targets_codes, global_targets_codes = target_codes.partition { |code| code.split(".")[2] } + global_targets = global_targets_codes.map { |code| SDG::Target[code] } + local_targets = local_targets_codes.map { |code| SDG::LocalTarget[code] } goals = goal_codes.map { |code| SDG::Goal[code] } transaction do - self.sdg_global_targets = targets - self.sdg_goals = (targets.map(&:goal) + goals).uniq + self.sdg_local_targets = local_targets + self.sdg_global_targets = global_targets + self.sdg_goals = (global_targets.map(&:goal) + local_targets.map(&:goal) + goals).uniq end end end diff --git a/app/models/sdg/goal.rb b/app/models/sdg/goal.rb index c342dde35204..544046af0e01 100644 --- a/app/models/sdg/goal.rb +++ b/app/models/sdg/goal.rb @@ -4,6 +4,7 @@ class SDG::Goal < ApplicationRecord validates :code, presence: true, uniqueness: true, inclusion: { in: 1..17 } has_many :targets, dependent: :destroy + has_many :local_targets, through: :targets def title I18n.t("sdg.goals.goal_#{code}.title") diff --git a/app/models/sdg/local_target.rb b/app/models/sdg/local_target.rb index 6711519d1f99..fbc9cbce7b05 100644 --- a/app/models/sdg/local_target.rb +++ b/app/models/sdg/local_target.rb @@ -2,8 +2,6 @@ class SDG::LocalTarget < ApplicationRecord include Comparable include SDG::Related - delegate :goal, to: :target - translates :title, touch: true translates :description, touch: true include Globalizable @@ -14,8 +12,10 @@ class SDG::LocalTarget < ApplicationRecord validates :code, presence: true, uniqueness: true, format: ->(local_target) { /\A#{local_target.target&.code}\.\d+/ } validates :target, presence: true + validates :goal, presence: true belongs_to :target + belongs_to :goal def self.[](code) find_by!(code: code) diff --git a/db/migrate/20210123100638_add_goals_to_local_targets.rb b/db/migrate/20210123100638_add_goals_to_local_targets.rb new file mode 100644 index 000000000000..b74846f3e685 --- /dev/null +++ b/db/migrate/20210123100638_add_goals_to_local_targets.rb @@ -0,0 +1,5 @@ +class AddGoalsToLocalTargets < ActiveRecord::Migration[5.2] + def change + add_reference :sdg_local_targets, :goal + end +end diff --git a/db/schema.rb b/db/schema.rb index c06db2e50367..25e23d4cbbd3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_01_07_125458) do +ActiveRecord::Schema.define(version: 2021_01_23_100638) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -1322,7 +1322,9 @@ t.string "code" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "goal_id" t.index ["code"], name: "index_sdg_local_targets_on_code", unique: true + t.index ["goal_id"], name: "index_sdg_local_targets_on_goal_id" t.index ["target_id"], name: "index_sdg_local_targets_on_target_id" end diff --git a/spec/factories/sdg.rb b/spec/factories/sdg.rb index 42073cf6782f..1f933eb0480b 100644 --- a/spec/factories/sdg.rb +++ b/spec/factories/sdg.rb @@ -13,6 +13,7 @@ sequence(:description) { |n| "Help for Local Target #{n}" } target { SDG::Target[code.rpartition(".").first] } + goal { SDG::Goal[code.split(".")[0]] } end factory :sdg_phase, class: "SDG::Phase" do diff --git a/spec/models/sdg/local_target_spec.rb b/spec/models/sdg/local_target_spec.rb index 523baa396d1a..5024d8522a06 100644 --- a/spec/models/sdg/local_target_spec.rb +++ b/spec/models/sdg/local_target_spec.rb @@ -18,7 +18,7 @@ end it "is not valid without a code" do - expect(build(:sdg_local_target, code: nil, target: SDG::Target[1.1])).not_to be_valid + expect(build(:sdg_local_target, code: nil, target: SDG::Target[1.1], goal: SDG::Goal[1])).not_to be_valid end it "is not valid when code does not include associated target code" do @@ -47,6 +47,10 @@ expect(build(:sdg_local_target, target: nil)).not_to be_valid end + it "is not valid without a goal" do + expect(build(:sdg_local_target, goal: nil)).not_to be_valid + end + describe "#goal" do it "returns the target goal" do local_target = create(:sdg_local_target, code: "1.1.1") diff --git a/spec/models/sdg/relatable_spec.rb b/spec/models/sdg/relatable_spec.rb index 4dd6aea511f4..1a456528d399 100644 --- a/spec/models/sdg/relatable_spec.rb +++ b/spec/models/sdg/relatable_spec.rb @@ -101,10 +101,11 @@ describe "#sdg_related_list" do it "orders related list by code" do - relatable.sdg_goals = [SDG::Goal[1], SDG::Goal[3], SDG::Goal[2]] - relatable.sdg_targets = [SDG::Target[2.2], SDG::Target[1.2], SDG::Target[2.1]] + relatable.sdg_goals = [SDG::Goal[1], SDG::Goal[2], SDG::Goal[3]] + local_targets = %w[2.2.2 2.2.1 3.1.1].map { |code| create(:sdg_local_target, code: code) } + relatable.sdg_targets = [SDG::Target[2.2], SDG::Target[1.2], SDG::Target[2.1]] + local_targets - expect(relatable.sdg_related_list).to eq "1, 1.2, 2, 2.1, 2.2, 3" + expect(relatable.sdg_related_list).to eq "1, 1.2, 2, 2.1, 2.2, 2.2.1, 2.2.2, 3, 3.1.1" end end @@ -133,6 +134,13 @@ expect(relatable.reload.sdg_targets).to match_array [SDG::Target[1.1]] end + it "assigns a single local target" do + relatable.sdg_related_list = local_target.code + + expect(relatable.reload.sdg_goals).to match_array [SDG::Goal[1]] + expect(relatable.reload.sdg_local_targets).to match_array [SDG::LocalTarget["1.2.1"]] + end + it "assigns multiple targets" do relatable.sdg_related_list = "1.1,2.3" @@ -146,6 +154,14 @@ expect(relatable.reload.sdg_goals).to match_array [SDG::Goal[1], SDG::Goal[2], SDG::Goal[3]] end + it "assigns a multiple local targets" do + relatable.sdg_related_list = "#{local_target.code}, #{another_local_target.code}" + + expect(relatable.reload.sdg_goals).to match_array [SDG::Goal[1], SDG::Goal[2]] + expect(relatable.reload.sdg_local_targets).to match_array [SDG::LocalTarget["1.2.1"], SDG::LocalTarget["2.3.1"]] + end + + it "ignores trailing spaces and spaces between commas" do relatable.sdg_related_list = " 1.1, 2.3 " @@ -153,11 +169,12 @@ expect(relatable.reload.sdg_targets).to match_array [SDG::Target[1.1], SDG::Target[2.3]] end - it "assigns goals and targets" do - relatable.sdg_related_list = "1.1,3,4,4.1" + it "assigns goals, targets and local_targets" do + relatable.sdg_related_list = "1.1,3,4,4.1,#{another_local_target.code}" - expect(relatable.reload.sdg_goals).to match_array [SDG::Goal[1], SDG::Goal[3], SDG::Goal[4]] - expect(relatable.reload.sdg_targets).to match_array [SDG::Target[1.1], SDG::Target[4.1]] + expect(relatable.reload.sdg_goals).to match_array [SDG::Goal[1], SDG::Goal[2], SDG::Goal[3], SDG::Goal[4]] + expect(relatable.reload.sdg_global_targets).to match_array [SDG::Target[1.1], SDG::Target[4.1]] + expect(relatable.reload.sdg_local_targets).to match_array [SDG::LocalTarget["2.3.1"]] end end