parent
3b0c4c188c
commit
c5bd1a9b3f
@ -0,0 +1,33 @@
|
||||
class Construct
|
||||
Zone = Struct.new(:low_benchmark, :high_benchmark, :type) do
|
||||
def includes_score?(score)
|
||||
score > low_benchmark and score < high_benchmark
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :title, :warning_zone, :watch_zone, :growth_zone, :approval_zone, :ideal_zone
|
||||
|
||||
def initialize(title:, watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:)
|
||||
@title = title
|
||||
@warning_zone = Zone.new(1, watch_low_benchmark, :warning)
|
||||
@watch_zone = Zone.new(watch_low_benchmark, growth_low_benchmark, :watch)
|
||||
@growth_zone = Zone.new(growth_low_benchmark, approval_low_benchmark, :growth)
|
||||
@approval_zone = Zone.new(approval_low_benchmark, ideal_low_benchmark, :approval)
|
||||
@ideal_zone = Zone.new(ideal_low_benchmark, 5, :ideal)
|
||||
end
|
||||
|
||||
def zone_for_score(score)
|
||||
case score
|
||||
when ideal_zone.low_benchmark..ideal_zone.high_benchmark
|
||||
ideal_zone
|
||||
when approval_zone.low_benchmark..approval_zone.high_benchmark
|
||||
approval_zone
|
||||
when growth_zone.low_benchmark..growth_zone.high_benchmark
|
||||
growth_zone
|
||||
when watch_zone.low_benchmark..watch_zone.high_benchmark
|
||||
watch_zone
|
||||
else
|
||||
warning_zone
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,36 @@
|
||||
module ConstructGraphParameters
|
||||
TOTAL_GRAPH_WIDTH = 1152
|
||||
GRAPH_WIDTH = 0.75 * TOTAL_GRAPH_WIDTH
|
||||
CONSTRUCT_ROW_HEIGHT = 40
|
||||
CONSTRUCT_ROW_BAR_HEIGHT = 20
|
||||
|
||||
module ZoneColor
|
||||
WARNING = "#FF73C0"
|
||||
WATCH = "#F096AD"
|
||||
GROWTH = "#E0BA9A"
|
||||
APPROVAL = "#D0DD86"
|
||||
IDEAL = "#C0FF73"
|
||||
end
|
||||
|
||||
class ZoneParams
|
||||
attr_reader :left_edge
|
||||
attr_reader :width
|
||||
|
||||
def initialize(left_edge:, width:)
|
||||
@left_edge = left_edge
|
||||
@width = width
|
||||
end
|
||||
|
||||
def right_edge
|
||||
left_edge + width
|
||||
end
|
||||
end
|
||||
|
||||
WARNING_ZONE = ZoneParams.new left_edge: 0, width: (GRAPH_WIDTH / 2) / 3
|
||||
WATCH_ZONE = ZoneParams.new left_edge: WARNING_ZONE.right_edge, width: (GRAPH_WIDTH / 2) / 3
|
||||
GROWTH_ZONE = ZoneParams.new left_edge: WATCH_ZONE.right_edge, width: (GRAPH_WIDTH / 2) / 3
|
||||
APPROVAL_ZONE = ZoneParams.new left_edge: GROWTH_ZONE.right_edge, width: (GRAPH_WIDTH / 2) / 2
|
||||
IDEAL_ZONE = ZoneParams.new left_edge: APPROVAL_ZONE.right_edge, width: (GRAPH_WIDTH / 2) / 2
|
||||
|
||||
KEY_BENCHMARK_WIDTH = 2
|
||||
end
|
||||
@ -0,0 +1,77 @@
|
||||
class ConstructGraphRowPresenter
|
||||
|
||||
def initialize(construct:, score:)
|
||||
@construct = construct
|
||||
@score = score
|
||||
end
|
||||
|
||||
def construct_title
|
||||
@construct.title
|
||||
end
|
||||
|
||||
def bar_color
|
||||
case zone.type
|
||||
when :ideal
|
||||
ConstructGraphParameters::ZoneColor::IDEAL
|
||||
when :approval
|
||||
ConstructGraphParameters::ZoneColor::APPROVAL
|
||||
when :growth
|
||||
ConstructGraphParameters::ZoneColor::GROWTH
|
||||
when :watch
|
||||
ConstructGraphParameters::ZoneColor::WATCH
|
||||
else
|
||||
ConstructGraphParameters::ZoneColor::WARNING
|
||||
end
|
||||
end
|
||||
|
||||
def bar_width
|
||||
percentage = (@score - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark)
|
||||
case zone.type
|
||||
when :ideal
|
||||
(percentage * ideal_zone_params.width + approval_zone_params.width).round
|
||||
when :approval
|
||||
(percentage * approval_zone_params.width).round
|
||||
when :growth
|
||||
(percentage * growth_zone_params.width).round
|
||||
when :watch
|
||||
(percentage * watch_zone_params.width + growth_zone_params.width).round
|
||||
else
|
||||
(percentage * warning_zone_params.width + watch_zone_params.width + growth_zone_params.width).round
|
||||
end
|
||||
end
|
||||
|
||||
def x_offset
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
0
|
||||
else
|
||||
bar_width
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def zone
|
||||
@construct.zone_for_score(@score)
|
||||
end
|
||||
|
||||
def ideal_zone_params
|
||||
ConstructGraphParameters::IDEAL_ZONE
|
||||
end
|
||||
|
||||
def approval_zone_params
|
||||
ConstructGraphParameters::APPROVAL_ZONE
|
||||
end
|
||||
|
||||
def growth_zone_params
|
||||
ConstructGraphParameters::GROWTH_ZONE
|
||||
end
|
||||
|
||||
def watch_zone_params
|
||||
ConstructGraphParameters::WATCH_ZONE
|
||||
end
|
||||
|
||||
def warning_zone_params
|
||||
ConstructGraphParameters::WARNING_ZONE
|
||||
end
|
||||
end
|
||||
@ -1 +1,54 @@
|
||||
<h1><%= @school.name %></h1>
|
||||
<h1><%= @school.name %></h1>
|
||||
|
||||
<% heading_gutter = 30 %>
|
||||
<% graph_height = @construct_graph_row_presenters.length * ConstructGraphParameters::CONSTRUCT_ROW_HEIGHT + heading_gutter %>
|
||||
<svg viewbox="0 0 <%= ConstructGraphParameters::TOTAL_GRAPH_WIDTH %> <%= graph_height %>" width=<%= ConstructGraphParameters::TOTAL_GRAPH_WIDTH %> height=<%= graph_height %> xmlns="http://www.w3.org/2000/svg">
|
||||
<% graph_center = ConstructGraphParameters::GRAPH_WIDTH / 2 %>
|
||||
<% label_padding_right = 24 %>
|
||||
|
||||
<% warning_zone = ConstructGraphParameters::WARNING_ZONE %>
|
||||
<% watch_zone = ConstructGraphParameters::WATCH_ZONE %>
|
||||
<% growth_zone = ConstructGraphParameters::GROWTH_ZONE %>
|
||||
<% approval_zone = ConstructGraphParameters::APPROVAL_ZONE %>
|
||||
<% ideal_zone = ConstructGraphParameters::IDEAL_ZONE %>
|
||||
|
||||
<% key_benchmark_width = ConstructGraphParameters::KEY_BENCHMARK_WIDTH %>
|
||||
<% key_benchmark_left_edge = graph_center - key_benchmark_width / 2 %>
|
||||
|
||||
<% construct_row_height = ConstructGraphParameters::CONSTRUCT_ROW_HEIGHT %>
|
||||
<% construct_row_bar_height = ConstructGraphParameters::CONSTRUCT_ROW_BAR_HEIGHT %>
|
||||
|
||||
<svg id="graph-background" x="25%" y="0" width="75%">
|
||||
<g id="scale-headings">
|
||||
<text x=<%= warning_zone.left_edge + warning_zone.width / 2 %> y=<%= heading_gutter / 2 %> text-anchor="middle" dominant-baseline="middle">Warning</text>
|
||||
<text x=<%= watch_zone.left_edge + watch_zone.width / 2 %> y=<%= heading_gutter / 2 %> text-anchor="middle" dominant-baseline="middle">Watch</text>
|
||||
<text x=<%= growth_zone.left_edge + growth_zone.width / 2 %> y=<%= heading_gutter / 2 %> text-anchor="middle" dominant-baseline="middle">Growth</text>
|
||||
<text x=<%= approval_zone.left_edge + approval_zone.width / 2 %> y=<%= heading_gutter / 2 %> text-anchor="middle" dominant-baseline="middle">Approval</text>
|
||||
<text x=<%= ideal_zone.left_edge + ideal_zone.width / 2 %> y=<%= heading_gutter / 2 %> text-anchor="middle" dominant-baseline="middle">Ideal</text>
|
||||
</g>
|
||||
|
||||
<g id="scale-background" transform="translate(0, <%= heading_gutter %>)">
|
||||
<rect id="warning-zone" x=<%= warning_zone.left_edge %> y="0" width=<%= warning_zone.width %> height="100%" fill=<%= ConstructGraphParameters::ZoneColor::WARNING %> fill-opacity="0.2" />
|
||||
<rect id="watch-zone" x=<%= watch_zone.left_edge %> y="0" width=<%= watch_zone.width %> height="100%" fill=<%= ConstructGraphParameters::ZoneColor::WATCH %> fill-opacity="0.2" />
|
||||
<rect id="growth-zone" x=<%= growth_zone.left_edge %> y="0" width=<%= growth_zone.width %> height="100%" fill=<%= ConstructGraphParameters::ZoneColor::GROWTH %> fill-opacity="0.2" />
|
||||
<rect id="approval-zone" x=<%= approval_zone.left_edge %> y="0" width=<%= approval_zone.width %> height="100%" fill=<%= ConstructGraphParameters::ZoneColor::APPROVAL %> fill-opacity="0.2" />
|
||||
<rect id="ideal-zone" x=<%= ideal_zone.left_edge %> y="0" width=<%= ideal_zone.width %> height="100%" fill=<%= ConstructGraphParameters::ZoneColor::IDEAL %> fill-opacity="0.2" />
|
||||
|
||||
<rect id="key-benchmark" x=<%= key_benchmark_left_edge %> y="0" width=<%= key_benchmark_width %> height="100%" fill="black" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<g id="construct-rows">
|
||||
<svg id="construct-row-labels" x="0" y=<%= heading_gutter %>>
|
||||
<%= @construct_graph_row_presenters.each_with_index do |presenter, index| %>
|
||||
<text x="25%" dx=<%= -1 * label_padding_right %> y=<%= index * construct_row_height + construct_row_height / 2 %> text-anchor="end" dominant-baseline="middle"><%= presenter.construct_title %></text>
|
||||
<% end %>
|
||||
</svg>
|
||||
|
||||
<svg id="construct-row-bars" x="25%" y=<%= heading_gutter %>>
|
||||
<%= @construct_graph_row_presenters.each_with_index do |presenter, index| %>
|
||||
<rect x=<%= graph_center - presenter.x_offset %> y=<%= index * construct_row_height + (construct_row_height - construct_row_bar_height) / 2 %> width="<%= presenter.bar_width %>" height=<%= construct_row_bar_height %> fill=<%= presenter.bar_color %> />
|
||||
<% end %>
|
||||
</svg>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe "construct graph row presenter" do
|
||||
|
||||
let(:watch_low_benchmark) { 2.9 }
|
||||
let(:growth_low_benchmark) { 3.1 }
|
||||
let(:approval_low_benchmark) { 3.6 }
|
||||
let(:ideal_low_benchmark) { 3.8 }
|
||||
|
||||
let(:construct) {
|
||||
Construct.new(
|
||||
title: 'Some Title',
|
||||
watch_low_benchmark: watch_low_benchmark,
|
||||
growth_low_benchmark: growth_low_benchmark,
|
||||
approval_low_benchmark: approval_low_benchmark,
|
||||
ideal_low_benchmark: ideal_low_benchmark
|
||||
)
|
||||
}
|
||||
|
||||
let(:presenter) {
|
||||
ConstructGraphRowPresenter.new construct: construct, score: score
|
||||
}
|
||||
|
||||
shared_examples_for 'construct_title' do
|
||||
it('returns the construct title') do
|
||||
expect(presenter.construct_title).to eq 'Some Title'
|
||||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Ideal zone') do
|
||||
let(:score) { 4.4 }
|
||||
|
||||
it_behaves_like 'construct_title'
|
||||
|
||||
it('returns the correct color') do
|
||||
expect(presenter.bar_color).to eq ConstructGraphParameters::ZoneColor::IDEAL
|
||||
end
|
||||
|
||||
it('returns a bar width equal to the approval zone width plus the proportionate ideal zone width') do
|
||||
expect(presenter.bar_width).to eq 324
|
||||
end
|
||||
|
||||
it('returns an x-offset of 0') do
|
||||
expect(presenter.x_offset).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Approval zone') do
|
||||
let(:score) { 3.7 }
|
||||
|
||||
it_behaves_like 'construct_title'
|
||||
|
||||
it("returns the correct color") do
|
||||
expect(presenter.bar_color).to eq ConstructGraphParameters::ZoneColor::APPROVAL
|
||||
end
|
||||
|
||||
it('returns a bar width equal to the proportionate approval zone width') do
|
||||
expect(presenter.bar_width).to eq 108
|
||||
end
|
||||
|
||||
it('returns an x-offset of 0') do
|
||||
expect(presenter.x_offset).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Growth zone') do
|
||||
let(:score) { 3.2 }
|
||||
|
||||
it_behaves_like 'construct_title'
|
||||
|
||||
it("returns the correct color") do
|
||||
expect(presenter.bar_color).to eq ConstructGraphParameters::ZoneColor::GROWTH
|
||||
end
|
||||
|
||||
it('returns a bar width equal to the proportionate growth zone width') do
|
||||
expect(presenter.bar_width).to eq 29
|
||||
end
|
||||
|
||||
it('returns an x-offset equal to the bar width') do
|
||||
expect(presenter.x_offset).to eq 29
|
||||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Watch zone') do
|
||||
let(:score) { 3.0 }
|
||||
|
||||
it_behaves_like 'construct_title'
|
||||
|
||||
it("returns the correct color") do
|
||||
expect(presenter.bar_color).to eq ConstructGraphParameters::ZoneColor::WATCH
|
||||
end
|
||||
|
||||
it('returns a bar width equal to the proportionate watch zone width plus the growth zone width') do
|
||||
expect(presenter.bar_width).to eq 216
|
||||
end
|
||||
|
||||
it('returns an x-offset equal to the bar width') do
|
||||
expect(presenter.x_offset).to be > 0
|
||||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Warning zone') do
|
||||
let(:score) { 2.8 }
|
||||
|
||||
it_behaves_like 'construct_title'
|
||||
|
||||
it("returns the correct color") do
|
||||
expect(presenter.bar_color).to eq ConstructGraphParameters::ZoneColor::WARNING
|
||||
end
|
||||
|
||||
it('returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths') do
|
||||
expect(presenter.bar_width).to eq 424
|
||||
end
|
||||
|
||||
it('returns an x-offset equal to the bar width') do
|
||||
expect(presenter.x_offset).to eq 424
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in new issue