sqm-dashboards/app/models/report/measure_summary.rb
rebuilt 0f457becf0 feat: create a parents by language graph
Update demographics table with lanugage options

Create a lanugage table to hold the new languages

Update the demographic loader to input languages into the database

Update the cleaner to read the language column

Update the parent table to hold a reference to a language

Update the data uploader script to read the language from the csv and update the language information for any parent items that already exist (or create database entries if none already exist)

update the analyze interface to add controls for selecting ‘parents by group’ and a dropdown for ‘parent by language’

Update the analyze controller to read the parent-by-group parameter

Create a graph for the parent-by-group view

Bubble up averages for language calculations.

Make sure n-size only counts responses for a given measure.
2025-04-28 16:42:11 -07:00

151 lines
5.6 KiB
Ruby

module Report
class MeasureSummary
def self.create_report(schools:, academic_years: AcademicYear.all, measures: ::Measure.all.order(measure_id: :ASC), filename: "measure_summary.csv")
data = to_csv(schools:, academic_years:, measures:)
FileUtils.mkdir_p Rails.root.join("tmp", "reports")
filepath = Rails.root.join("tmp", "reports", filename)
write_csv(data:, filepath:)
data
end
def self.to_csv(schools:, academic_years:, measures:)
data = []
mutex = Thread::Mutex.new
data << ["Measure Name", "Measure ID", "District", "Academic Year", "Recorded Date Range", "Grades", "Student Score", "Student Zone", "Teacher Score",
"Teacher Zone", "Admin Score", "Admin Zone", "All Score (Average)", "All Score Zone"]
pool_size = 2
jobs = Queue.new
measures.each { |measure| jobs << measure }
workers = pool_size.times.map do
Thread.new do
while measure = jobs.pop(true)
academic_years.each do |academic_year|
respondents = Respondent.where(school: schools, academic_year:)
enrollment = respondents.map do | respondent| respondent.enrollment_by_grade.keys end.flatten.compact.uniq.sort
grades_with_responses = ::SurveyItemResponse.where(school: schools, academic_year:).where.not(grade: nil).pluck(:grade).uniq.sort
all_grades = (enrollment & grades_with_responses).sort
grades = "#{all_grades.first}-#{all_grades.last}"
begin_date = ::SurveyItemResponse.where(school: schools,
academic_year:).where.not(recorded_date: nil).order(:recorded_date).first&.recorded_date&.to_date
end_date = ::SurveyItemResponse.where(school: schools,
academic_year:).where.not(recorded_date: nil).order(:recorded_date).last&.recorded_date&.to_date
date_range = "#{begin_date} - #{end_date}"
district = schools.first.district
row = [measure, district, academic_year]
mutex.synchronize do
data << [measure.name,
measure.measure_id,
district.name,
academic_year.range,
date_range,
grades,
student_score(row:),
student_zone(row:),
teacher_score(row:),
teacher_zone(row:),
admin_score(row:),
admin_zone(row:),
all_data_score(row:),
all_data_zone(row:)]
end
end
end
rescue ThreadError
end
end
workers.each(&:join)
CSV.generate do |csv|
data.each do |row|
csv << row
end
end
end
def self.write_csv(data:, filepath:)
File.write(filepath, csv)
end
def self.all_data_score(row:)
row in [ measure, district, academic_year]
score = district.schools.map do |school|
score = measure.score(school:, academic_year:).average
end.remove_blanks.average
score || "N/A"
end
def self.all_data_zone(row:)
row in [ measure, district, academic_year]
average = all_data_score(row:)
score = Score.new(average:, meets_teacher_threshold: true, meets_student_threshold: true,
meets_admin_data_threshold: true)
student_zone = measure.zone_for_score(score:).type.to_s
student_zone || "N/A"
end
def self.student_score(row:)
row in [ measure, district, academic_year]
student_score = district.schools.map do |school|
student_score = measure.student_score(school:, academic_year:).average
end.remove_blanks.average
student_score || "N/A"
end
def self.student_zone(row:)
row in [ measure, district, academic_year]
average = student_score(row:)
score = Score.new(average:, meets_teacher_threshold: true, meets_student_threshold: true,
meets_admin_data_threshold: true)
student_zone = measure.zone_for_score(score:).type.to_s
student_zone || "N/A"
end
def self.teacher_score(row:)
row in [ measure, district, academic_year]
teacher_score = district.schools.map do |school|
measure.teacher_score(school:, academic_year:).average
end.remove_blanks.average
teacher_score || "N/A"
end
def self.teacher_zone(row:)
row in [ measure, district, academic_year]
average = teacher_score(row:)
score = Score.new(average:, meets_teacher_threshold: true, meets_student_threshold: true,
meets_admin_data_threshold: true)
teacher_zone = measure.zone_for_score(score:).type.to_s
teacher_zone || "N/A"
end
def self.admin_score(row:)
row in [ measure, district, academic_year]
admin_score = district.schools.map do |school|
measure.admin_score(school:, academic_year:).average
end.remove_blanks.average
admin_score = "N/A" unless admin_score.present? && admin_score >= 0
admin_score
end
def self.admin_zone(row:)
row in [ measure, district, academic_year]
average = admin_score(row:)
score = Score.new(average:, meets_teacher_threshold: true, meets_student_threshold: true,
meets_admin_data_threshold: true)
tmp_zone = measure.zone_for_score(score:).type.to_s
tmp_zone == :insufficient_data ? "N/A" : tmp_zone.to_s.capitalize
end
end
end