mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
Create grouped bar chart on analyze page
This commit is contained in:
parent
d7b0fe0e36
commit
7a9830915b
20 changed files with 204 additions and 4 deletions
|
|
@ -5,5 +5,8 @@ class AnalyzeController < SqmApplicationController
|
|||
|
||||
@subcategory ||= Subcategory.find_by_subcategory_id(params[:subcategory_id])
|
||||
@subcategory ||= Subcategory.find_by_subcategory_id('1A')
|
||||
|
||||
@measure = @subcategory.measures.first
|
||||
@academic_year ||= AcademicYear.order('range DESC').first
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class CategoriesController < SqmApplicationController
|
||||
helper GaugeHelper
|
||||
def show
|
||||
@categories = Category.sorted.map { |category| CategoryPresenter.new(category:) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class HomeController < ApplicationController
|
||||
helper HeaderHelper
|
||||
def index
|
||||
@districts = District.all.order(:name)
|
||||
@schools = School.all.includes([:district]).order(:name)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
class OverviewController < SqmApplicationController
|
||||
before_action :check_empty_dataset, only: [:index]
|
||||
helper VarianceHelper
|
||||
|
||||
def index
|
||||
@variance_chart_row_presenters = Measure.all.map(&method(:presenter_for_measure))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
class SqmApplicationController < ApplicationController
|
||||
protect_from_forgery with: :exception, prepend: true
|
||||
before_action :set_schools_and_districts
|
||||
helper HeaderHelper
|
||||
|
||||
private
|
||||
|
||||
|
|
|
|||
57
app/helpers/analyze_helper.rb
Normal file
57
app/helpers/analyze_helper.rb
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
module AnalyzeHelper
|
||||
def zone_label_width
|
||||
15
|
||||
end
|
||||
|
||||
def zone_label_x
|
||||
2
|
||||
end
|
||||
|
||||
def graph_height
|
||||
85
|
||||
end
|
||||
|
||||
def svg_height
|
||||
400
|
||||
end
|
||||
|
||||
def graph_width
|
||||
85
|
||||
end
|
||||
|
||||
def benchmark_y
|
||||
(zone_height * 2) - (benchmark_height / 2.0)
|
||||
end
|
||||
|
||||
def benchmark_height
|
||||
1
|
||||
end
|
||||
|
||||
def grouped_chart_width
|
||||
graph_width / data_sources
|
||||
end
|
||||
|
||||
def grouped_chart_divider_x(position)
|
||||
zone_label_width + (grouped_chart_width * position)
|
||||
end
|
||||
|
||||
def bar_label_height
|
||||
(100 - ((100 - graph_height) / 2))
|
||||
end
|
||||
|
||||
def bar_label_x(position)
|
||||
zone_label_width + (grouped_chart_width * position) - (grouped_chart_width / 2)
|
||||
end
|
||||
|
||||
def zone_height
|
||||
graph_height / 5
|
||||
end
|
||||
|
||||
def zone_label_y(position)
|
||||
8.5 * (position + position - 1)
|
||||
end
|
||||
|
||||
def data_sources
|
||||
3
|
||||
end
|
||||
end
|
||||
|
|
@ -135,7 +135,6 @@ class Measure < ActiveRecord::Base
|
|||
averages << student_survey_items.first.send(name) if includes_student_survey_items?
|
||||
averages << teacher_survey_items.first.send(name) if includes_teacher_survey_items?
|
||||
(averages << admin_data_items.map(&name)).flatten! if includes_admin_data_items?
|
||||
|
||||
averages.average
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
belongs_to :survey_item
|
||||
|
||||
scope :exclude_boston, lambda {
|
||||
boston = District.where(name: 'Boston').first
|
||||
boston = District.find_by_name('Boston')
|
||||
where.not(school: boston.schools) if boston.present?
|
||||
}
|
||||
end
|
||||
|
|
|
|||
59
app/presenters/grouped_bar_chart_presenter.rb
Normal file
59
app/presenters/grouped_bar_chart_presenter.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
class GroupedBarChartPresenter
|
||||
attr_reader :score
|
||||
|
||||
def initialize(measure:, score:)
|
||||
@measure = measure
|
||||
@score = score.average
|
||||
@meets_teacher_threshold = score.meets_teacher_threshold?
|
||||
@meets_student_threshold = score.meets_student_threshold?
|
||||
@measure_name = @measure.name
|
||||
@measure_id = @measure.measure_id
|
||||
@category = @measure.subcategory.category
|
||||
end
|
||||
|
||||
IDEAL_ZONE_WIDTH_PERCENTAGE = 0.17
|
||||
APPROVAL_ZONE_WIDTH_PERCENTAGE = 0.17
|
||||
GROWTH_ZONE_WIDTH_PERCENTAGE = 0.17
|
||||
WATCH_ZONE_WIDTH_PERCENTAGE = 0.17
|
||||
WARNING_ZONE_WIDTH_PERCENTAGE = 0.17
|
||||
|
||||
def y_offset
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
34 - bar_height_percentage * 100
|
||||
else
|
||||
34
|
||||
end
|
||||
end
|
||||
|
||||
def bar_height_percentage
|
||||
case zone.type
|
||||
when :ideal
|
||||
percentage * IDEAL_ZONE_WIDTH_PERCENTAGE + APPROVAL_ZONE_WIDTH_PERCENTAGE
|
||||
when :approval
|
||||
percentage * APPROVAL_ZONE_WIDTH_PERCENTAGE
|
||||
when :growth
|
||||
(1 - percentage) * GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
when :watch
|
||||
(1 - percentage) * WATCH_ZONE_WIDTH_PERCENTAGE + GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
when :warning
|
||||
(1 - percentage) * WARNING_ZONE_WIDTH_PERCENTAGE + WATCH_ZONE_WIDTH_PERCENTAGE + GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
else
|
||||
0.0
|
||||
end
|
||||
end
|
||||
|
||||
def percentage
|
||||
(@score - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark)
|
||||
end
|
||||
|
||||
def zone
|
||||
zones = Zones.new(
|
||||
watch_low_benchmark: @measure.watch_low_benchmark,
|
||||
growth_low_benchmark: @measure.growth_low_benchmark,
|
||||
approval_low_benchmark: @measure.approval_low_benchmark,
|
||||
ideal_low_benchmark: @measure.ideal_low_benchmark
|
||||
)
|
||||
zones.zone_for_score(@score)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +1,74 @@
|
|||
<% content_for :title do %>
|
||||
<div class="sub-header-2 color-white m-0"> Analysis of <%= @school.name %> </div>
|
||||
<% end %>
|
||||
|
||||
<% presenter = GroupedBarChartPresenter.new(measure: @measure, score: @measure.score(school: @school, academic_year: @academic_year)) %>
|
||||
<div class="graph-content">
|
||||
<div class="breadcrumbs">
|
||||
<div class="breadcrumbs sub-header-4">
|
||||
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<div class="mt-6" >
|
||||
<p class="construct-id">Measure <%= @measure.measure_id %></p>
|
||||
<span class="sub-header-2">
|
||||
<%= @measure.name %>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<svg width="100%" height="<%= svg_height %>" >
|
||||
<g>
|
||||
<rect x="0" y="0" width="100%" height="<%= zone_height * 2 %>%" fill="#edecf0"/>
|
||||
<rect x="0" y="<%= zone_height * 2 %>%" width="100%" height="<%= zone_height * 3 %>%" fill="#fffaee"/>
|
||||
<rect x="0" y="0" width="100%" height="<%= graph_height %>%" fill="none" stroke="grey"/>
|
||||
<line x1="<%= grouped_chart_divider_x(1) %>%" y1="0" x2="<%= grouped_chart_divider_x(1) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
|
||||
<line x1="<%= grouped_chart_divider_x(2) %>%" y1="0" x2="<%= grouped_chart_divider_x(2) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
|
||||
<rect x="0" y="<%= benchmark_y %>%" width="100%" height="<%= benchmark_height %>%" fill="black"/>
|
||||
</g>
|
||||
|
||||
<g stroke-width="1" >
|
||||
<line x1="0" y1="17%" x2="100%" y2="17%" stroke="white" />
|
||||
<line x1="0" y1="51%" x2="100%" y2="51%" stroke="#edecf0" />
|
||||
<line x1="0" y1="68%" x2="100%" y2="68%" stroke="#edecf0" />
|
||||
</g>
|
||||
|
||||
<g >
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(1) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Ideal
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(2) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Approval
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(3) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Growth
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(4) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Watch
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(5) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Warning
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g>
|
||||
<text class="graph-footer" x="<%= bar_label_x(1) %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle">
|
||||
All Students
|
||||
</text>
|
||||
<text class="graph-footer" x="<%= bar_label_x(2) %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle">
|
||||
All Teachers
|
||||
</text>
|
||||
<text class="graph-footer" x="<%= bar_label_x(3) %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle">
|
||||
All Survey Data
|
||||
</text>
|
||||
</g>
|
||||
|
||||
|
||||
<g>
|
||||
<rect x="<%= bar_label_x(3) - 2.5 %>%" y="<%= presenter.y_offset %>%" width="5%" height="<%= presenter.bar_height_percentage * 100 %>%" fill="#3E3A38"/>
|
||||
<text x="<%= bar_label_x(3) %>%" y="<%= 5 %>%" text-anchor="middle" dominant-baseline="middle" >
|
||||
<%= presenter.score %>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,4 +69,6 @@ Rails.application.configure do
|
|||
# Use an evented file watcher to asynchronously detect changes in source code,
|
||||
# routes, locales, etc. This feature depends on the listen gem.
|
||||
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||
|
||||
config.action_controller.include_all_helpers = false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -109,4 +109,6 @@ Rails.application.configure do
|
|||
# config.active_record.database_selector = { delay: 2.seconds }
|
||||
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
||||
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
||||
|
||||
config.action_controller.include_all_helpers = false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -52,4 +52,6 @@ Rails.application.configure do
|
|||
|
||||
# Raises error for missing translations.
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
config.action_controller.include_all_helpers = false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include VarianceHelper
|
||||
|
||||
describe OverviewController, type: :controller do
|
||||
include BasicAuthHelper
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include GaugeHelper
|
||||
|
||||
describe 'categories/show' do
|
||||
before :each do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include SchedulesHelper
|
||||
|
||||
module Legacy
|
||||
RSpec.describe 'legacy/schedules/edit', type: :view do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include SchedulesHelper
|
||||
|
||||
module Legacy
|
||||
RSpec.describe 'legacy/schedules/new', type: :view do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include SchedulesHelper
|
||||
|
||||
module Legacy
|
||||
RSpec.describe 'legacy/schedules/show', type: :view do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include VarianceHelper
|
||||
|
||||
describe 'overview/index' do
|
||||
subject { Nokogiri::HTML(rendered) }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
require 'rails_helper'
|
||||
include VarianceHelper
|
||||
|
||||
describe 'overview/_variance_chart.html.erb' do
|
||||
before do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue