diff --git a/app/models/measure.rb b/app/models/measure.rb index dad2fd37..22c0ad4e 100644 --- a/app/models/measure.rb +++ b/app/models/measure.rb @@ -25,13 +25,13 @@ class Measure < ActiveRecord::Base end def student_survey_items_with_sufficient_responses(school:, academic_year:) - SurveyItem.where(id: SurveyItem.joins("inner join survey_item_responses on survey_item_responses.survey_item_id = survey_items.id") + SurveyItem.where(id: SurveyItem.joins('inner join survey_item_responses on survey_item_responses.survey_item_id = survey_items.id') .student_survey_items .where("survey_item_responses.school": school, - "survey_item_responses.academic_year": academic_year, - "survey_item_responses.survey_item_id": survey_items.student_survey_items) - .group("survey_items.id") - .having("count(*) >= 10") + "survey_item_responses.academic_year": academic_year, + "survey_item_responses.survey_item_id": survey_items.student_survey_items) + .group('survey_items.id') + .having('count(*) >= 10') .count.keys) end @@ -123,6 +123,14 @@ class Measure < ActiveRecord::Base any_admin_data_collected?(school:, academic_year:) end + def benchmark(name) + averages = [] + 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 + private def any_admin_data_collected?(school:, academic_year:) @@ -181,14 +189,6 @@ class Measure < ActiveRecord::Base @grouped_responses[[school, academic_year]] end - def benchmark(name) - averages = [] - 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 - def sufficient_student_data?(school:, academic_year:) return false unless includes_student_survey_items? return false if no_student_responses_exist?(school:, academic_year:) @@ -222,7 +222,7 @@ class Measure < ActiveRecord::Base def incalculable_score(school:, academic_year:) @incalculable_score ||= Hash.new do |memo, (school, academic_year)| lacks_sufficient_survey_data = !sufficient_student_data?(school:, academic_year:) && - !sufficient_teacher_data?(school:, academic_year:) + !sufficient_teacher_data?(school:, academic_year:) memo[[school, academic_year]] = lacks_sufficient_survey_data && !includes_admin_data_items? end diff --git a/app/models/report/subcategory.rb b/app/models/report/subcategory.rb new file mode 100644 index 00000000..5ab3b04d --- /dev/null +++ b/app/models/report/subcategory.rb @@ -0,0 +1,42 @@ +module Report + class Subcategory + def self.create_report(schools: School.all, academic_years: AcademicYear.all, subcategories: ::Subcategory.all) + data = [] + data << ['School', 'Academic Year', 'Subcategory', 'Student Score', 'Student Zone', 'Teacher Score', + 'Teacher Zone', 'Admin Score', 'Admin Zone', 'All Score (Average)', 'All Score Zone'] + schools.each do |school| + academic_years.each do |academic_year| + next if SurveyItemResponse.where(school:, academic_year:).count.zero? + + subcategories.each do |subcategory| + student_score = subcategory.student_score(school:, academic_year:) + student_zone = subcategory.student_zone(school:, academic_year:).type.to_s.capitalize + teacher_score = subcategory.teacher_score(school:, academic_year:) + teacher_zone = subcategory.teacher_zone(school:, academic_year:).type.to_s.capitalize + admin_score = subcategory.admin_score(school:, academic_year:) + admin_zone = subcategory.admin_zone(school:, academic_year:).type.to_s.capitalize + score = subcategory.score(school:, academic_year:) + zone = subcategory.zone(school:, academic_year:).type.to_s.capitalize + + data << [school.name, academic_year.range, subcategory.subcategory_id, + student_score, student_zone, teacher_score, teacher_zone, admin_score, admin_zone, score, zone] + end + end + end + + FileUtils.mkdir_p Rails.root.join('tmp', 'reports') + filepath = Rails.root.join('tmp', 'reports', 'subcategories.csv') + write_csv(data:, filepath:) + data + end + + def self.write_csv(data:, filepath:) + csv = CSV.generate do |csv| + data.each do |row| + csv << row + end + end + File.write(filepath, csv) + end + end +end diff --git a/app/models/subcategory.rb b/app/models/subcategory.rb index 988eb286..267bb3a3 100644 --- a/app/models/subcategory.rb +++ b/app/models/subcategory.rb @@ -14,6 +14,24 @@ class Subcategory < ActiveRecord::Base scores.average end + def student_score(school:, academic_year:) + measures.map do |measure| + measure.student_score(school:, academic_year:).average + end.compact.average + end + + def teacher_score(school:, academic_year:) + measures.map do |measure| + measure.teacher_score(school:, academic_year:).average + end.compact.average + end + + def admin_score(school:, academic_year:) + measures.map do |measure| + measure.admin_score(school:, academic_year:).average + end.compact.average + end + def response_rate(school:, academic_year:) @response_rate ||= Hash.new do |memo, (school, academic_year)| student = StudentResponseRateCalculator.new(subcategory: self, school:, academic_year:) @@ -25,4 +43,51 @@ class Subcategory < ActiveRecord::Base @response_rate[[school, academic_year]] end + + def warning_low_benchmark + 1 + end + + def watch_low_benchmark + @watch_low_benchmark ||= benchmark(:watch_low_benchmark) + end + + def growth_low_benchmark + @growth_low_benchmark ||= benchmark(:growth_low_benchmark) + end + + def approval_low_benchmark + @approval_low_benchmark ||= benchmark(:approval_low_benchmark) + end + + def ideal_low_benchmark + @ideal_low_benchmark ||= benchmark(:ideal_low_benchmark) + end + + def benchmark(name) + measures.map do |measure| + measure.benchmark(name) + end.average + end + + def zone(school:, academic_year:) + zone_for_score(score: score(school:, academic_year:)) + end + + def student_zone(school:, academic_year:) + zone_for_score(score: student_score(school:, academic_year:)) + end + + def teacher_zone(school:, academic_year:) + zone_for_score(score: teacher_score(school:, academic_year:)) + end + + def admin_zone(school:, academic_year:) + zone_for_score(score: admin_score(school:, academic_year:)) + end + + def zone_for_score(score:) + Zones.new(watch_low_benchmark:, growth_low_benchmark:, + approval_low_benchmark:, ideal_low_benchmark:).zone_for_score(score) + end end diff --git a/spec/models/report b/spec/models/report deleted file mode 100644 index e69de29b..00000000 diff --git a/spec/models/report/subcategory_spec.rb b/spec/models/report/subcategory_spec.rb new file mode 100644 index 00000000..6c3dd25e --- /dev/null +++ b/spec/models/report/subcategory_spec.rb @@ -0,0 +1,35 @@ +require 'rails_helper' +RSpec.describe Report::Subcategory, type: :model do + let(:school) { create(:school, name: 'Milford High', slug: 'milford-high') } + let(:academic_year) { create(:academic_year, range: '2018-2019') } + let(:subcategory) { create(:subcategory, subcategory_id: '1A') } + before :each do + school + academic_year + subcategory + end + let(:measure) { create(:measure, subcategory:) } + let(:scale) { create(:scale, measure:) } + let(:survey_item) { create(:student_survey_item, scale:) } + + context 'when creating a report for a subcategory' do + before :each do + create_list(:survey_item_response, 10, survey_item:, school:, academic_year:) + end + + it 'creates a report for subcategories' do + expect(Report::Subcategory.create_report).to be_a(Array) + headers = Report::Subcategory.create_report.first + expect(headers).to eq(['School', 'Academic Year', 'Subcategory', 'Student Score', + 'Student Zone', 'Teacher Score', 'Teacher Zone', 'Admin Score', 'Admin Zone', 'All Score (Average)', 'All Score Zone']) + end + + it 'Adds information about the first school and first academic year to the report' do + report = Report::Subcategory.create_report + report[1] in [school_name, academic_year, subcategory_id, *] + expect(school_name).to eq('Milford High') + expect(academic_year).to eq('2018-2019') + expect(subcategory_id).to eq('1A') + end + end +end