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