diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index b709ee86..a19b53ad 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,3 +1,4 @@ @import "bootstrap"; @import "scaffolds"; @import "recipients"; +@import "school_categories"; diff --git a/app/assets/stylesheets/school_categories.scss b/app/assets/stylesheets/school_categories.scss new file mode 100644 index 00000000..2e5d54a2 --- /dev/null +++ b/app/assets/stylesheets/school_categories.scss @@ -0,0 +1,104 @@ +.school_category { + border: 1px solid black; + + .title { + height: 3.5em; + } + + .description { + height: 6em; + } +} + +.indicator { + padding-top: 6px; + height: 90px; + + .indicator-circle { + height: 100%; + position: relative; + + .indicator-zones { + width: 100%; + height: 100%; + + div { + height: 100%; + width: 20%; + float: left; + } + + .zone0 { + background-color: orange; + } + .zone1 { + background-color: yellow; + } + .zone2 { + background-color: lightgreen; + } + .zone3 { + background-color: green; + } + .zone4 { + background: repeating-linear-gradient(45deg, lightgray, green 15px); + } + } + + .average { + position: absolute; + width: 10%; + height: 125%; + top: -3px; + border: 2px dashed black; + font-weight: bold; + font-size: 12px; + + span { + text-align: center; + position: absolute; + width: 7.75em; + } + } + } +} + +.indicator { + height: 30px; + + .indicator-circle { + width: 100%; + height: 100%; + + .average { + span { + top: -13px; + left: -2.5em; + font-size: 10px; + border: none; + line-height: 10px; + } + } + } + + &.small { + height: 90px; + + .indicator-circle { + width: 90px; + overflow: hidden; + + .indicator-zones { + width: 900px; + } + + .average { + border: none; + text-align: center; + width: 100%; + line-height: 90px; + } + } + } + +} diff --git a/app/controllers/schools_controller.rb b/app/controllers/schools_controller.rb index 1248d14c..1921c5d7 100644 --- a/app/controllers/schools_controller.rb +++ b/app/controllers/schools_controller.rb @@ -10,6 +10,7 @@ class SchoolsController < ApplicationController # GET /schools/1 # GET /schools/1.json def show + @school_categories = @school.school_categories.for_parent_category(nil).sort end # GET /schools/new diff --git a/app/models/category.rb b/app/models/category.rb index 611ed265..270c59e7 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -3,7 +3,28 @@ class Category < ApplicationRecord has_many :questions belongs_to :parent_category, class_name: 'Category', foreign_key: :parent_category_id has_many :child_categories, class_name: 'Category', foreign_key: :parent_category_id + has_many :school_categories validates :name, presence: true + scope :for_parent, -> (category=nil) { where(parent_category_id: category.try(:id)) } + + def root_identifier + p = self + while p.parent_category.present? + p = p.parent_category + end + p.name.downcase.gsub(/\s/, '-') + end + + def root_index + [ + "teachers-and-the-teaching-environment", + "school-culture", + "resources", + "indicators-of-academic-learning", + "character-and-wellbeing-outcomes" + ].index(root_identifier) + end + end diff --git a/app/models/school.rb b/app/models/school.rb index 72edd4e2..8b297c97 100644 --- a/app/models/school.rb +++ b/app/models/school.rb @@ -3,6 +3,7 @@ class School < ApplicationRecord has_many :recipient_lists belongs_to :district has_many :recipients + has_many :school_categories validates :name, presence: true diff --git a/app/models/school_category.rb b/app/models/school_category.rb index eb2af60f..b8f00148 100644 --- a/app/models/school_category.rb +++ b/app/models/school_category.rb @@ -7,6 +7,11 @@ class SchoolCategory < ApplicationRecord validates_associated :category scope :for, -> (school, category) { where(school: school).where(category: category) } + scope :for_parent_category, -> (category=nil) { joins(:category).merge(Category.for_parent(category)) } + + def answer_index_average + answer_index_total.to_f / response_count.to_f + end def aggregated_responses attempt_data = Attempt. @@ -47,7 +52,11 @@ class SchoolCategory < ApplicationRecord return if ENV['BULK_PROCESS'] update_attributes(chained_aggregated_responses) if category.parent_category.present? - SchoolCategory.for(school, category.parent_category).each { |sc| sc.sync_aggregated_responses } + parent_school_category = SchoolCategory.for(school, category.parent_category).first + if parent_school_category.nil? + parent_school_category = SchoolCategory.create(school: school, category: category.parent_category) + end + parent_school_category.sync_aggregated_responses end end end diff --git a/app/views/school_categories/_indicator.html.haml b/app/views/school_categories/_indicator.html.haml new file mode 100644 index 00000000..0afcecad --- /dev/null +++ b/app/views/school_categories/_indicator.html.haml @@ -0,0 +1,34 @@ +- num_likerts = 5 +- likert = school_category.answer_index_average +- average_offset = (likert/num_likerts) * 100 +- zones = ['Warning Zone: Schools that fall in this range are five or more years away from reaching community-wide targets. Consequently, this zone, as established by teachers, principals, parents, and district administrators, indicates that a school is in significant need of improvement. If the school is not in the Warning Zone in other areas, it may be a relatively successful school overall. Still, it must immediately develop a plan for addressing its shortcomings.', 'Watch Zone: Schools falling in this range are three or four years away from reaching community-wide targets. This zone, established by teachers, principals, parents, and district administrators, is not an ideal place for schools to be. But it does not mean that the school is failing. Instead, it means that the school needs to place particular emphasis on improving its work in this area.', 'Growth Zone: Schools falling in this range earned scores just below "acceptable." Yet these schools are close enough to the Approval Zone that they might reasonably reach it within two years. As established by teachers, principals, parents, and district administrators, this zone is an acceptable place for schools to be if they have the leadership, focus, and resources to improve their work in a particular area.', 'Approval Zone: Schools falling in this range earned scores between "acceptable" and "ideal." This zone, established by teachers, principals, parents, and district administrators, is the target that all schools should be striving to hit. Scoring in this range does not mean that a school is perfect; but it does mean that it is meeting or exceeding community-wide expectations in a particular category.', 'This area represents a set of outcomes so close to perfect that they are unlikely to be realized in any school.'] +- approval_zone = [[76, 90], [77, 91], [71, 85], [73, 86], [73, 86]][school_category.category.root_index] + +- zone_widths = [0] +- zone_widths << approval_zone[0] - (15 + 15) +- zone_widths << 15 +- zone_widths << 15 +- zone_widths << approval_zone[1] - approval_zone[0] +- zone_widths << 100 - approval_zone[1] + +- buffer = 0.25 +- this_school_zone = "This school scored in the range between #{(likert - buffer).round(2)} and #{(likert + buffer).round(2)}.\n\nMouse over each color to read a description of each zone." + +.indicator{class: (local_assigns[:small] ? 'small' : '')} + .indicator-circle + .indicator-zones{style: "margin-left: #{local_assigns[:small] ? ((average_offset - 5) * -10) : 0}%"} + - zone_total = 0 + - zones.each_with_index do |zone, index| + %div{class: "zone#{index}", style: "width: #{zone_widths[index+1]}%", title: "#{zone}\n\nThis zone: #{(zone_total/20.0).round(2)} - #{((zone_total + zone_widths[index+1])/20.0).round(2)}\nThis school: #{(likert - buffer).round(2)} - #{(likert + buffer).round(2)}"} + - zone_total += zone_widths[index + 1] + - if local_assigns[:small] + -# + .average{title: this_school_zone} + This School + - else + .average{style: "left: #{average_offset - 5}%", title: this_school_zone} + %span This School + +- if local_assigns[:caption] + .explanation + * Mouse-over each colored area for detailed description. diff --git a/app/views/school_categories/_school_category.html.haml b/app/views/school_categories/_school_category.html.haml new file mode 100644 index 00000000..c943d9da --- /dev/null +++ b/app/views/school_categories/_school_category.html.haml @@ -0,0 +1,13 @@ +.col-4.py-3 + .school_category.p-2 + %h4.title.text-center.pt-3 + = link_to(school_category.category.name, [school_category.school, school_category.category]) + + .indicator-container + = render 'school_categories/indicator', school_category: school_category + + .description.px-2.pt-3.pb-2 + - if false #(measurements = school_category.questions.measurements.for_school(school_measure.school)).present? + = render 'measurements/nonlikert', measurement: measurements.first, description: school_measure.measure.description + - else + = school_category.category.blurb || truncate(school_category.category.description, length: 108) diff --git a/app/views/schools/show.html.haml b/app/views/schools/show.html.haml index 96a86490..a34604d7 100644 --- a/app/views/schools/show.html.haml +++ b/app/views/schools/show.html.haml @@ -3,6 +3,10 @@ %p %strong Name: = @school.name - %p - %strong District: - = @school.district_id + - if @school.district.present? + %p + %strong District: + = link_to @school.district.name, @school.district + +.row + = render @school_categories diff --git a/spec/models/school_category_spec.rb b/spec/models/school_category_spec.rb index e84a4e71..16a98aa6 100644 --- a/spec/models/school_category_spec.rb +++ b/spec/models/school_category_spec.rb @@ -35,7 +35,13 @@ RSpec.describe SchoolCategory, type: :model do end end - describe 'sync_responses' do + describe 'answer_index_average' do + it 'should provide the average answer_index for all responses' do + expect(school_category1.answer_index_average).to eq(15.0/4.0) + end + end + + describe 'sync_aggregated_responses' do let!(:category3) { Category.create!(name: 'Category 3', parent_category: category1) } diff --git a/spec/views/schools/show.html.erb_spec.rb b/spec/views/schools/show.html.erb_spec.rb index 99f7081f..30ae8872 100644 --- a/spec/views/schools/show.html.erb_spec.rb +++ b/spec/views/schools/show.html.erb_spec.rb @@ -2,15 +2,16 @@ require 'rails_helper' RSpec.describe "schools/show", type: :view do before(:each) do + @school_categories = [] @school = assign(:school, School.create!( - :name => "Name", - :district_id => 2 + :name => "School", + :district => District.create(name: 'District') )) end it "renders attributes in

" do render - expect(rendered).to match(/Name/) - expect(rendered).to match(/2/) + expect(rendered).to match(/School/) + expect(rendered).to match(/District/) end end