From 87ca23df791248b40413ab9d8ca882aec1fc5919 Mon Sep 17 00:00:00 2001 From: rebuilt Date: Fri, 19 May 2023 16:50:10 -0700 Subject: [PATCH] Add subcategory report --- app/models/report/subcategory.rb | 76 ++++++++++++++++++++++---- app/models/subcategory.rb | 24 ++++++-- lib/tasks/report.rake | 6 ++ spec/models/report/subcategory_spec.rb | 56 +++++++++++++++++++ 4 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 lib/tasks/report.rake diff --git a/app/models/report/subcategory.rb b/app/models/report/subcategory.rb index 5ab3b04d..fa2d898a 100644 --- a/app/models/report/subcategory.rb +++ b/app/models/report/subcategory.rb @@ -1,31 +1,38 @@ module Report class Subcategory - def self.create_report(schools: School.all, academic_years: AcademicYear.all, subcategories: ::Subcategory.all) + def self.create_report(schools: School.all, academic_years: AcademicYear.all, subcategories: ::Subcategory.all, filename: 'subcategories.csv') 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 + next if Respondent.where(school:, academic_year:).empty? + + response_rate = subcategory.response_rate(school:, academic_year:) + next unless response_rate.meets_student_threshold? || response_rate.meets_teacher_threshold? + 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] + row = [response_rate, subcategory, school, academic_year] + data << [school.name, + academic_year.range, + subcategory.subcategory_id, + student_score(row:), + student_zone(row:), + teacher_score(row:), + teacher_zone(row:), + admin_score(row:), + admin_zone(row:), + score, + zone] end end end FileUtils.mkdir_p Rails.root.join('tmp', 'reports') - filepath = Rails.root.join('tmp', 'reports', 'subcategories.csv') + filepath = Rails.root.join('tmp', 'reports', filename) write_csv(data:, filepath:) data end @@ -38,5 +45,50 @@ module Report end File.write(filepath, csv) end + + def self.student_score(row:) + row in [response_rate, subcategory, school, academic_year] + student_score = subcategory.student_score(school:, academic_year:) if response_rate.meets_student_threshold? + student_score || 'N/A' + end + + def self.student_zone(row:) + row in [response_rate, subcategory, school, academic_year] + if response_rate.meets_student_threshold? + student_zone = subcategory.student_zone(school:, + academic_year:).type.to_s.capitalize + end + + student_zone || 'N/A' + end + + def self.teacher_score(row:) + row in [response_rate, subcategory, school, academic_year] + teacher_score = subcategory.teacher_score(school:, academic_year:) if response_rate.meets_teacher_threshold? + + teacher_score || 'N/A' + end + + def self.teacher_zone(row:) + row in [response_rate, subcategory, school, academic_year] + if response_rate.meets_teacher_threshold? + teacher_zone = subcategory.teacher_zone(school:, academic_year:).type.to_s.capitalize + end + + teacher_zone || 'N/A' + end + + def self.admin_score(row:) + row in [response_rate, subcategory, school, academic_year] + admin_score = subcategory.admin_score(school:, academic_year:) + admin_score = 'N/A' unless admin_score >= 0 + admin_score + end + + def self.admin_zone(row:) + row in [response_rate, subcategory, school, academic_year] + tmp_zone = subcategory.admin_zone(school:, academic_year:).type + tmp_zone == :insufficient_data ? 'N/A' : tmp_zone.to_s.capitalize + end end end diff --git a/app/models/subcategory.rb b/app/models/subcategory.rb index 267bb3a3..07b1ce09 100644 --- a/app/models/subcategory.rb +++ b/app/models/subcategory.rb @@ -7,11 +7,27 @@ class Subcategory < ActiveRecord::Base has_many :survey_items, through: :measures def score(school:, academic_year:) - scores = measures.map do |measure| + measures.map do |measure| measure.score(school:, academic_year:).average - end - scores = scores.reject(&:nil?) - scores.average + end.remove_blanks.average + end + + def student_score(school:, academic_year:) + measures.map do |measure| + measure.student_score(school:, academic_year:).average + end.remove_blanks.average + end + + def teacher_score(school:, academic_year:) + measures.map do |measure| + measure.teacher_score(school:, academic_year:).average + end.remove_blanks.average + end + + def admin_score(school:, academic_year:) + measures.map do |measure| + measure.admin_score(school:, academic_year:).average + end.remove_blanks.average end def student_score(school:, academic_year:) diff --git a/lib/tasks/report.rake b/lib/tasks/report.rake new file mode 100644 index 00000000..b5b04fa9 --- /dev/null +++ b/lib/tasks/report.rake @@ -0,0 +1,6 @@ +namespace :report do + desc 'create a report of the scores for all subcategories' + task subcategory: :environment do + Report::Subcategory.create_report(filename: 'ecp_subcategory_report.csv') + end +end diff --git a/spec/models/report/subcategory_spec.rb b/spec/models/report/subcategory_spec.rb index 6c3dd25e..339fff5e 100644 --- a/spec/models/report/subcategory_spec.rb +++ b/spec/models/report/subcategory_spec.rb @@ -3,10 +3,12 @@ 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') } + let(:respondent) { create(:respondent, school:, academic_year:) } before :each do school academic_year subcategory + respondent end let(:measure) { create(:measure, subcategory:) } let(:scale) { create(:scale, measure:) } @@ -32,4 +34,58 @@ RSpec.describe Report::Subcategory, type: :model do expect(subcategory_id).to eq('1A') end end + describe '.create_report' do + before do + allow(Report::Subcategory).to receive(:write_csv) + end + + it 'generates a CSV report' do + expect(FileUtils).to receive(:mkdir_p).with(Rails.root.join('tmp', 'reports')) + + Report::Subcategory.create_report + + expect(Report::Subcategory).to have_received(:write_csv) + end + + it 'returns the report data' do + data = Report::Subcategory.create_report + + expect(data).to be_an(Array) + end + end + describe '.write_csv' do + it 'writes the data to a CSV file' do + data = [['School', 'Academic Year', 'Subcategory'], ['School A', '2022-2023', 'Category A']] + filepath = Rails.root.join('tmp', 'spec', 'reports', 'subcategories.csv') + + FileUtils.mkdir_p Rails.root.join('tmp', 'spec', 'reports') + Report::Subcategory.write_csv(data:, filepath:) + + csv_data = File.read(filepath) + expect(csv_data).to include('School,Academic Year,Subcategory') + expect(csv_data).to include('School A,2022-2023,Category A') + end + end + + describe '.student_score' do + let(:response_rate) { create(:response_rate, subcategory:, school:, academic_year:) } + let(:row) { [response_rate, subcategory, school, academic_year] } + + it 'returns student score if response rate meets student threshold' do + allow(subcategory).to receive(:student_score).and_return(80) + allow(response_rate).to receive(:meets_student_threshold?).and_return(true) + + score = Report::Subcategory.student_score(row:) + + expect(score).to eq(80) + end + + it 'returns "N/A" if response rate does not meet student threshold' do + allow(response_rate).to receive(:meets_student_threshold?).and_return(false) + + score = Report::Subcategory.student_score(row:) + + expect(score).to eq('N/A') + end + end end