diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 40881803..b308b5da 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -3,6 +3,9 @@ class DashboardController < ApplicationController
def index
authenticate(district.name.downcase, "#{district.name.downcase}!")
+ @construct_graph_row_presenters = [
+ ConstructGraphRowPresenter.new(construct: professional_qualifications_construct, score: 4.8)
+ ]
end
private
@@ -16,7 +19,11 @@ class DashboardController < ApplicationController
end
def district
- @school.district
+ @district ||= @school.district
end
-end
\ No newline at end of file
+ def professional_qualifications_construct
+ Construct.new title: "Professional Qualifications", watch_low_benchmark: 2.48, growth_low_benchmark: 2.99, approval_low_benchmark: 3.49, ideal_low_benchmark: 4.7
+ end
+
+end
diff --git a/app/models/construct.rb b/app/models/construct.rb
new file mode 100644
index 00000000..7f594936
--- /dev/null
+++ b/app/models/construct.rb
@@ -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
diff --git a/app/models/construct_graph_parameters.rb b/app/models/construct_graph_parameters.rb
new file mode 100644
index 00000000..7b3e2e03
--- /dev/null
+++ b/app/models/construct_graph_parameters.rb
@@ -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
diff --git a/app/presenters/construct_graph_row_presenter.rb b/app/presenters/construct_graph_row_presenter.rb
new file mode 100644
index 00000000..f2734d34
--- /dev/null
+++ b/app/presenters/construct_graph_row_presenter.rb
@@ -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
diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb
index 7fb1b868..8d3bd669 100644
--- a/app/views/dashboard/index.html.erb
+++ b/app/views/dashboard/index.html.erb
@@ -1 +1,54 @@
-
<%= @school.name %>
\ No newline at end of file
+
<%= @school.name %>
+
+<% heading_gutter = 30 %>
+<% graph_height = @construct_graph_row_presenters.length * ConstructGraphParameters::CONSTRUCT_ROW_HEIGHT + heading_gutter %>
+
diff --git a/spec/presenters/construct_graph_row_presenter_spec.rb b/spec/presenters/construct_graph_row_presenter_spec.rb
new file mode 100644
index 00000000..46f3975d
--- /dev/null
+++ b/spec/presenters/construct_graph_row_presenter_spec.rb
@@ -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