diff --git a/app/assets/stylesheets/school_categories.scss b/app/assets/stylesheets/school_categories.scss
index bfb69d16..8aa29e5b 100644
--- a/app/assets/stylesheets/school_categories.scss
+++ b/app/assets/stylesheets/school_categories.scss
@@ -1,4 +1,4 @@
-.school_category {
+.school_category, .question {
border: 1px solid black;
.title {
@@ -63,6 +63,26 @@
}
}
}
+
+ &.small {
+ height: 87px;
+
+ .indicator-circle {
+ width: 90px;
+ overflow: hidden;
+
+ .indicator-zones {
+ width: 900px;
+ }
+
+ .average {
+ border: none;
+ text-align: center;
+ width: 100%;
+ line-height: 90px;
+ }
+ }
+ }
}
&.short {
@@ -87,27 +107,3 @@
}
}
}
-
-// .indicator {
-//
-// &.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/categories_controller.rb b/app/controllers/categories_controller.rb
index bd0fd5ea..9f1d13ae 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -13,6 +13,7 @@ class CategoriesController < ApplicationController
def show
@school_category = SchoolCategory.for(@school, @category).first
@child_school_categories = SchoolCategory.for_parent_category(@school, @category)
+ @questions = @category.questions
end
# GET /categories/new
diff --git a/app/models/attempt.rb b/app/models/attempt.rb
index 6cd53878..618c60c0 100644
--- a/app/models/attempt.rb
+++ b/app/models/attempt.rb
@@ -11,6 +11,7 @@ class Attempt < ApplicationRecord
scope :for_category, -> (category) { joins(:question).merge(Question.for_category(category)) }
scope :for_school, -> (school) { joins(:recipient).merge(Recipient.for_school(school)) }
+ scope :with_responses, -> { where('answer_index is not null')}
def send_message
twilio_number = ENV['TWILIO_NUMBER']
diff --git a/app/models/question.rb b/app/models/question.rb
index 8ad5c702..e234a9b7 100644
--- a/app/models/question.rb
+++ b/app/models/question.rb
@@ -1,6 +1,18 @@
+AggregatedResponses = Struct.new(
+ :question,
+ :category,
+ :responses,
+ :count,
+ :answer_index_total,
+ :answer_index_average,
+ :most_popular_answer
+)
+
class Question < ApplicationRecord
belongs_to :category
+ has_many :attempts
+
validates :text, presence: true
validates :option1, presence: true
validates :option2, presence: true
@@ -10,7 +22,6 @@ class Question < ApplicationRecord
scope :for_category, -> (category) { where(category: category) }
-
def options
[option1, option2, option3, option4, option5].map(&:downcase).map(&:strip)
end
@@ -18,4 +29,25 @@ class Question < ApplicationRecord
def option_index(answer)
options.index(answer.downcase.strip)
end
+
+ def aggregated_responses_for_school(school)
+ school_responses = attempts.for_school(school).with_responses
+ return unless school_responses.present?
+
+ response_answer_total = school_responses.inject(0) { |total, response| total + response.answer_index }
+
+ histogram = school_responses.group_by(&:answer_index)
+ most_popular_answer_index = histogram.to_a.sort_by { |info| info[1].length }.last[0]
+ most_popular_answer = send("option#{most_popular_answer_index}")
+
+ AggregatedResponses.new(
+ self,
+ category,
+ school_responses,
+ school_responses.length,
+ response_answer_total,
+ response_answer_total.to_f / school_responses.length.to_f,
+ most_popular_answer
+ )
+ end
end
diff --git a/app/views/categories/show.html.haml b/app/views/categories/show.html.haml
index 7280807c..3ee3da10 100644
--- a/app/views/categories/show.html.haml
+++ b/app/views/categories/show.html.haml
@@ -23,8 +23,12 @@
(out of 5)
.indicator-container.py-3
- = render 'school_categories/indicator', school_category: @school_category
+ = render 'school_categories/indicator', info: @school_category
- if @child_school_categories.present?
.row
= render @child_school_categories
+
+- if @questions.present?
+ .row
+ = render @questions
diff --git a/app/views/questions/_question.html.haml b/app/views/questions/_question.html.haml
new file mode 100644
index 00000000..10ad2895
--- /dev/null
+++ b/app/views/questions/_question.html.haml
@@ -0,0 +1,38 @@
+- aggregated_responses = question.aggregated_responses_for_school(@school)
+- return if aggregated_responses.nil?
+
+.col-6.py-3
+ .question.p-2{id: "question-#{question.id}"}
+ %p.question-text.pt-3.px-2
+ %b Question:
+ = question.text
+
+ .pb-3.px-2
+ .indicator-container.float-left
+ = render 'school_categories/indicator', info: aggregated_responses, small: true
+
+ .pl-3.pt-1.float-left
+ %p
+ %b Total Responses:
+ = aggregated_responses.count
+ %p
+ %b Most Popular:
+ = truncate(aggregated_responses.most_popular_answer, length: 27)
+ %p
+ %b Source:
+ =# question.source(@school)
+
+ .clearfix
+
+ -#
+ .show-hide
+ %p
+ %span{class: 'toggle', data: {toggle: 'histogram-answers'}}= "Show Histogram".html_safe
+ |
+ %span{class: 'toggle', data: {toggle: 'raw'}}= "Show Raw Data".html_safe
+
+ - if question.is_a?(Question) || question.is_a?(GroupedQuestion)
+ .histogram-answers.hidden
+ = render question
+
+ %p.raw.hidden= likerts.join(', ')
diff --git a/app/views/school_categories/_indicator.html.haml b/app/views/school_categories/_indicator.html.haml
index 0afcecad..8a8b30af 100644
--- a/app/views/school_categories/_indicator.html.haml
+++ b/app/views/school_categories/_indicator.html.haml
@@ -1,8 +1,8 @@
- num_likerts = 5
-- likert = school_category.answer_index_average
+- likert = info.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]
+- approval_zone = [[76, 90], [77, 91], [71, 85], [73, 86], [73, 86]][info.category.root_index]
- zone_widths = [0]
- zone_widths << approval_zone[0] - (15 + 15)
diff --git a/app/views/school_categories/_school_category.html.haml b/app/views/school_categories/_school_category.html.haml
index 2abf7618..8793097f 100644
--- a/app/views/school_categories/_school_category.html.haml
+++ b/app/views/school_categories/_school_category.html.haml
@@ -4,7 +4,7 @@
= link_to(school_category.category.name, [school_category.school, school_category.category])
.indicator-container.short
- = render 'school_categories/indicator', school_category: school_category
+ = render 'school_categories/indicator', info: school_category
.description.px-2.pt-3.pb-2.mt-2
- if false #(measurements = school_category.questions.measurements.for_school(school_measure.school)).present?
diff --git a/spec/models/question_spec.rb b/spec/models/question_spec.rb
new file mode 100644
index 00000000..b6f7a854
--- /dev/null
+++ b/spec/models/question_spec.rb
@@ -0,0 +1,54 @@
+require 'rails_helper'
+
+RSpec.describe Question, type: :model do
+
+ let!(:school1) { School.create!(name: 'School 1') }
+ let!(:school2) { School.create!(name: 'School 2') }
+
+ let!(:school1recipients) { create_recipients(school1, 5) }
+ let!(:school2recipients) { create_recipients(school2, 4) }
+
+ let!(:category1) { Category.create!(name: 'Resources') }
+ let!(:category2) { Category.create!(name: 'Category 2') }
+
+ let!(:category1questions) { create_questions(3, category1) }
+ let!(:category2questions) { create_questions(3, category2) }
+ let(:question) { category1questions.first }
+
+ let!(:attempt1) { Attempt.create!(question: category1questions[0], recipient: school1recipients[0], answer_index: 3)}
+ let!(:attempt2) { Attempt.create!(question: category1questions[0], recipient: school1recipients[1], answer_index: 2)}
+ let!(:attempt3) { Attempt.create!(question: category1questions[0], recipient: school1recipients[2])}
+ let!(:attempt4) { Attempt.create!(question: category1questions[0], recipient: school1recipients[3], answer_index: 3)}
+ let!(:attempt5) { Attempt.create!(question: category1questions[0], recipient: school2recipients[0], answer_index: 4)}
+ let!(:attempt6) { Attempt.create!(question: category1questions[1], recipient: school1recipients[0], answer_index: 5)}
+ let!(:attempt7) { Attempt.create!(question: category1questions[2], recipient: school1recipients[0], answer_index: 5)}
+ let!(:attempt8) { Attempt.create!(question: category2questions[0], recipient: school1recipients[0], answer_index: 3)}
+ let!(:attempt9) { Attempt.create!(question: category2questions[1], recipient: school1recipients[1], answer_index: 1)}
+
+ describe 'aggregated_responses_for_school' do
+
+ let(:aggregated_responses) { question.aggregated_responses_for_school(school1) }
+
+ it 'aggregates all attempts with responses for the question for a given school' do
+ expect(aggregated_responses.count).to eq(3)
+ expect(aggregated_responses.responses.to_a).to eq([attempt1, attempt2, attempt4])
+ expect(aggregated_responses.answer_index_total).to eq(8)
+ end
+
+ it 'should calculate answer_index_average' do
+ expect(aggregated_responses.answer_index_average).to eq(8.0 / 3)
+ end
+
+ it 'should calculate the most popular answer' do
+ expect(aggregated_responses.most_popular_answer).to eq(question.option3)
+ end
+
+ it 'should provide access to the question and category' do
+ expect(aggregated_responses.question).to eq(question)
+ expect(aggregated_responses.category).to eq(question.category)
+ end
+
+ end
+
+
+end