Merge pull request #4322 from consul/sdg_local_filter
Allow filtering by local target in SDG management
This commit is contained in:
@@ -53,6 +53,8 @@ class SDGManagement::Relations::IndexComponent < ApplicationComponent
|
||||
end
|
||||
|
||||
def target_options
|
||||
options_from_collection_for_select(SDG::Target.all.sort, :code, :code, params[:target_code])
|
||||
targets = SDG::Target.all + SDG::LocalTarget.all
|
||||
|
||||
options_from_collection_for_select(targets.sort, :code, :code, params[:target_code])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,29 +4,39 @@ module SDG::Relatable
|
||||
included do
|
||||
has_many :sdg_relations, as: :relatable, dependent: :destroy, class_name: "SDG::Relation"
|
||||
|
||||
%w[SDG::Goal SDG::Target SDG::LocalTarget].each do |sdg_type|
|
||||
%w[SDG::Goal SDG::LocalTarget].each do |sdg_type|
|
||||
has_many sdg_type.constantize.table_name.to_sym,
|
||||
through: :sdg_relations,
|
||||
source: :related_sdg,
|
||||
source_type: sdg_type
|
||||
end
|
||||
has_many :sdg_global_targets,
|
||||
through: :sdg_relations,
|
||||
source: :related_sdg,
|
||||
source_type: "SDG::Target"
|
||||
|
||||
has_one :sdg_review, as: :relatable, dependent: :destroy, class_name: "SDG::Review"
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def by_goal(code)
|
||||
by_sdg_related(SDG::Goal, code)
|
||||
by_sdg_related(:sdg_goals, code)
|
||||
end
|
||||
|
||||
def by_target(code)
|
||||
by_sdg_related(SDG::Target, code)
|
||||
if SDG::Target.find_by(code: code)
|
||||
by_sdg_related(:sdg_global_targets, code)
|
||||
else
|
||||
by_sdg_related(:sdg_local_targets, code)
|
||||
end
|
||||
end
|
||||
|
||||
def by_sdg_related(sdg_class, code)
|
||||
def by_sdg_related(association, code)
|
||||
return all if code.blank?
|
||||
|
||||
joins(sdg_class.table_name.to_sym).merge(sdg_class.where(code: code))
|
||||
sdg_class = reflect_on_association(association).options[:source_type].constantize
|
||||
|
||||
joins(association).merge(sdg_class.where(code: code))
|
||||
end
|
||||
|
||||
def sdg_reviewed
|
||||
@@ -42,6 +52,19 @@ module SDG::Relatable
|
||||
sdg_relations.map(&:related_sdg)
|
||||
end
|
||||
|
||||
def sdg_targets
|
||||
sdg_global_targets + sdg_local_targets
|
||||
end
|
||||
|
||||
def sdg_targets=(targets)
|
||||
global_targets, local_targets = targets.partition { |target| target.class.name == "SDG::Target" }
|
||||
|
||||
transaction do
|
||||
self.sdg_global_targets = global_targets
|
||||
self.sdg_local_targets = local_targets
|
||||
end
|
||||
end
|
||||
|
||||
def sdg_goal_list
|
||||
sdg_goals.order(:code).map(&:code).join(", ")
|
||||
end
|
||||
@@ -52,7 +75,7 @@ module SDG::Relatable
|
||||
|
||||
def sdg_related_list
|
||||
sdg_goals.order(:code).map do |goal|
|
||||
[goal, sdg_targets.where(goal: goal).sort]
|
||||
[goal, sdg_global_targets.where(goal: goal).sort]
|
||||
end.flatten.map(&:code).join(", ")
|
||||
end
|
||||
|
||||
@@ -62,7 +85,7 @@ module SDG::Relatable
|
||||
goals = goal_codes.map { |code| SDG::Goal[code] }
|
||||
|
||||
transaction do
|
||||
self.sdg_targets = targets
|
||||
self.sdg_global_targets = targets
|
||||
self.sdg_goals = (targets.map(&:goal) + goals).uniq
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,10 +17,12 @@ class SDG::LocalTarget < ApplicationRecord
|
||||
|
||||
belongs_to :target
|
||||
|
||||
def <=>(local_target)
|
||||
return unless local_target.class == self.class
|
||||
|
||||
[target, numeric_subcode] <=> [local_target.target, local_target.numeric_subcode]
|
||||
def <=>(any_target)
|
||||
if any_target.class == self.class
|
||||
[target, numeric_subcode] <=> [any_target.target, any_target.numeric_subcode]
|
||||
elsif any_target.class == target.class
|
||||
-1 * (any_target <=> self)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
@@ -12,10 +12,12 @@ class SDG::Target < ApplicationRecord
|
||||
I18n.t("sdg.goals.goal_#{goal.code}.targets.target_#{code_key}.title")
|
||||
end
|
||||
|
||||
def <=>(target)
|
||||
return unless target.class == self.class
|
||||
|
||||
[goal.code, numeric_subcode] <=> [target.goal.code, target.numeric_subcode]
|
||||
def <=>(any_target)
|
||||
if any_target.class == self.class
|
||||
[goal.code, numeric_subcode] <=> [any_target.goal.code, any_target.numeric_subcode]
|
||||
elsif any_target.class.name == "SDG::LocalTarget"
|
||||
[self, -1] <=> [any_target.target, 1]
|
||||
end
|
||||
end
|
||||
|
||||
def self.[](code)
|
||||
|
||||
@@ -73,5 +73,13 @@ describe SDG::LocalTarget do
|
||||
expect(local_target).to be > lesser_local_target
|
||||
expect(local_target).to be < greater_local_target
|
||||
end
|
||||
|
||||
it "can be compared against global targets" do
|
||||
lesser_target = build(:sdg_target, code: "10.A", goal: SDG::Goal[10])
|
||||
greater_target = build(:sdg_target, code: "11.1", goal: SDG::Goal[11])
|
||||
|
||||
expect(local_target).to be > lesser_target
|
||||
expect(local_target).to be < greater_target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,9 +36,9 @@ describe SDG::Relatable do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_targets" do
|
||||
describe "#sdg_global_targets" do
|
||||
it "can assign targets to a model" do
|
||||
relatable.sdg_targets = [target, another_target]
|
||||
relatable.sdg_global_targets = [target, another_target]
|
||||
|
||||
expect(SDG::Relation.count).to be 2
|
||||
expect(SDG::Relation.first.relatable).to eq relatable
|
||||
@@ -48,9 +48,9 @@ describe SDG::Relatable do
|
||||
end
|
||||
|
||||
it "can obtain the list of targets" do
|
||||
relatable.sdg_targets = [target, another_target]
|
||||
relatable.sdg_global_targets = [target, another_target]
|
||||
|
||||
expect(relatable.reload.sdg_targets).to match_array [target, another_target]
|
||||
expect(relatable.reload.sdg_global_targets).to match_array [target, another_target]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,6 +60,25 @@ describe SDG::Relatable do
|
||||
|
||||
expect(relatable.sdg_target_list).to eq "1.2, 2.1, 2.2"
|
||||
end
|
||||
|
||||
it "includes both targets and local targets in order" do
|
||||
relatable.sdg_global_targets = [SDG::Target[2.2], SDG::Target[1.2], SDG::Target[2.1]]
|
||||
relatable.sdg_local_targets = %w[1.1.1 2.1.3].map { |code| create(:sdg_local_target, code: code) }
|
||||
|
||||
expect(relatable.sdg_target_list).to eq "1.1.1, 1.2, 2.1, 2.1.3, 2.2"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_targets=" do
|
||||
it "assigns both targets and local targets" do
|
||||
global_targets = [SDG::Target[2.2], SDG::Target[1.2]]
|
||||
local_targets = %w[2.2.1 3.1.1].map { |code| create(:sdg_local_target, code: code) }
|
||||
|
||||
relatable.sdg_targets = global_targets + local_targets
|
||||
|
||||
expect(relatable.sdg_global_targets).to match_array global_targets
|
||||
expect(relatable.sdg_local_targets).to match_array local_targets
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sdg_local_targets" do
|
||||
@@ -190,10 +209,23 @@ describe SDG::Relatable do
|
||||
|
||||
it "does not return records not associated with that target" do
|
||||
create(:proposal)
|
||||
create(:proposal, sdg_targets: [another_target])
|
||||
create(:proposal, sdg_global_targets: [another_target])
|
||||
|
||||
expect(relatable.class.by_target(target.code)).to be_empty
|
||||
end
|
||||
|
||||
it "returns records associated to a local target" do
|
||||
relatable.sdg_local_targets = [local_target]
|
||||
|
||||
expect(relatable.class.by_target(local_target.code)).to eq [relatable]
|
||||
end
|
||||
|
||||
it "does not return records not associated with that local target" do
|
||||
create(:proposal)
|
||||
create(:proposal, sdg_local_targets: [another_local_target])
|
||||
|
||||
expect(relatable.class.by_target(local_target.code)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pending_sdg_review" do
|
||||
|
||||
@@ -61,6 +61,32 @@ describe SDG::Target do
|
||||
expect(target).to be > lesser_target
|
||||
expect(target).to be < greater_target
|
||||
end
|
||||
|
||||
context "comparing with a local target" do
|
||||
it "compares using the goal first" do
|
||||
lesser_local_target = build(:sdg_local_target, code: "2.1.1")
|
||||
greater_local_target = build(:sdg_local_target, code: "11.1.2")
|
||||
|
||||
expect(target).to be > lesser_local_target
|
||||
expect(target).to be < greater_local_target
|
||||
end
|
||||
|
||||
it "compares using the target when the goal is the same" do
|
||||
lesser_target = build(:sdg_target, code: "10.2", goal: goal)
|
||||
greater_target = build(:sdg_target, code: "10.A", goal: goal)
|
||||
lesser_local_target = build(:sdg_local_target, code: "10.2.25", target: lesser_target)
|
||||
greater_local_target = build(:sdg_local_target, code: "10.A.1", target: greater_target)
|
||||
|
||||
expect(target).to be > lesser_local_target
|
||||
expect(target).to be < greater_local_target
|
||||
end
|
||||
|
||||
it "is smaller than a local target belonging to it" do
|
||||
local_target = build(:sdg_local_target, target: target, code: "10.19.1")
|
||||
|
||||
expect(target).to be < local_target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".[]" do
|
||||
|
||||
@@ -167,6 +167,21 @@ describe "SDG Relations", :js do
|
||||
expect(page).to have_css "li.is-active h2", exact_text: "Pending"
|
||||
end
|
||||
|
||||
scenario "local target filter" do
|
||||
schools = create(:sdg_local_target, code: "4.1.1")
|
||||
teachers = create(:sdg_local_target, code: "4.1.2")
|
||||
|
||||
create(:debate, title: "Rebuild local schools", sdg_local_targets: [schools])
|
||||
create(:debate, title: "Hire teachers", sdg_local_targets: [teachers])
|
||||
|
||||
visit sdg_management_debates_path
|
||||
select "4.1.1", from: "target_code"
|
||||
click_button "Search"
|
||||
|
||||
expect(page).to have_content "Rebuild local schools"
|
||||
expect(page).not_to have_content "Hire teachers"
|
||||
end
|
||||
|
||||
scenario "search within current tab" do
|
||||
visit sdg_management_proposals_path(filter: "pending_sdg_review")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user