mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-13 09:20:38 -07:00
cap the likert score at 5. This was happening already at the scraper level but it's also now being done by the admin data loader for safety. Also make sure to just update admin data instead of deleting and reloading all values each load. Add tests to confirm this behavior
208 lines
5.3 KiB
Ruby
208 lines
5.3 KiB
Ruby
class SurveyItemValues
|
|
attr_reader :row, :headers, :genders, :survey_items, :schools
|
|
|
|
def initialize(row:, headers:, genders:, survey_items:, schools:)
|
|
@row = row
|
|
@headers = headers
|
|
@genders = genders
|
|
@survey_items = survey_items
|
|
@schools = schools
|
|
end
|
|
|
|
def dese_id?
|
|
dese_id.present?
|
|
end
|
|
|
|
def recorded_date
|
|
@recorded_date ||= begin
|
|
recorded_date = value_from(pattern: /Recorded\s*Date/i)
|
|
Date.parse(recorded_date)
|
|
end
|
|
end
|
|
|
|
def academic_year
|
|
@academic_year ||= AcademicYear.find_by_date recorded_date
|
|
end
|
|
|
|
def survey_item_response(survey_item:)
|
|
@survey_item_response ||= Hash.new do |memo, survey_item|
|
|
memo[survey_item] = survey_item_responses[[response_id, survey_item.id]]
|
|
end
|
|
|
|
@survey_item_response[survey_item]
|
|
end
|
|
|
|
def survey_item_responses
|
|
@survey_item_responses ||= Hash.new do |memo|
|
|
responses_hash = {}
|
|
SurveyItemResponse.where(school:, academic_year:, response_id:).each do |response|
|
|
responses_hash[[response.response_id, response.survey_item.id]] = response
|
|
end
|
|
memo[[school, academic_year]] = responses_hash
|
|
end
|
|
|
|
@survey_item_responses[[school, academic_year]]
|
|
end
|
|
|
|
def response_id
|
|
@response_id ||= value_from(pattern: /Response\s*ID/i)
|
|
end
|
|
|
|
def dese_id
|
|
@dese_id ||= begin
|
|
dese_id = nil
|
|
dese_headers = ['DESE ID', 'Dese ID', 'DeseId', 'DeseID', 'School', 'school']
|
|
school_headers = headers.select { |header| /School-\s\w/.match(header) }
|
|
dese_headers << school_headers
|
|
dese_headers.flatten.each do |header|
|
|
dese_id ||= row[header]
|
|
end
|
|
|
|
dese_id.to_i
|
|
end
|
|
end
|
|
|
|
def likert_score(survey_item_id:)
|
|
row[survey_item_id] || row["#{survey_item_id}-1"]
|
|
end
|
|
|
|
def school
|
|
@school ||= schools[dese_id]
|
|
end
|
|
|
|
def district
|
|
@district ||= school&.district
|
|
end
|
|
|
|
def grade
|
|
@grade ||= begin
|
|
raw_grade = value_from(pattern: /Grade|What grade are you in?/i)
|
|
|
|
return nil if raw_grade.blank?
|
|
|
|
raw_grade.to_i
|
|
end
|
|
end
|
|
|
|
def gender
|
|
gender_code = value_from(pattern: /Gender|What is your gender?|What is your gender? - Selected Choice/i)
|
|
gender_code ||= 99
|
|
gender_code = gender_code.to_i
|
|
gender_code = 4 if gender_code == 3
|
|
gender_code = 99 if gender_code.zero?
|
|
genders[gender_code]
|
|
end
|
|
|
|
def value_from(pattern:)
|
|
output = nil
|
|
matches = headers.select do |header|
|
|
pattern.match(header)
|
|
end.map { |item| item.delete("\n") }
|
|
matches.each do |match|
|
|
output ||= row[match]
|
|
end
|
|
output
|
|
end
|
|
|
|
def to_a
|
|
copy_likert_scores_from_variant_survey_items
|
|
row.remove_unwanted_columns
|
|
end
|
|
|
|
def duration
|
|
@duration ||= value_from(pattern: /Duration|Duration \(in seconds\)|Duration\.\.\(in\.seconds\)/i)
|
|
end
|
|
|
|
def valid?
|
|
valid_duration? && valid_progress? && valid_grade? && valid_sd?
|
|
end
|
|
|
|
def respondent_type
|
|
return :teacher if headers
|
|
.filter(&:present?)
|
|
.filter { |header| header.start_with? 't-' }.count > 0
|
|
|
|
:student
|
|
end
|
|
|
|
def survey_type
|
|
survey_item_ids = headers
|
|
.filter(&:present?)
|
|
.filter { |header| header.start_with?('t-', 's-') }
|
|
|
|
SurveyItem.survey_type(survey_item_ids:)
|
|
end
|
|
|
|
def valid_duration?
|
|
return true if duration.nil? || duration == '' || duration.downcase == 'n/a' || duration.downcase == 'na'
|
|
|
|
span_in_seconds = duration.to_i
|
|
return span_in_seconds >= 300 if survey_type == :teacher
|
|
return span_in_seconds >= 240 if survey_type == :standard
|
|
return span_in_seconds >= 100 if survey_type == :short_form
|
|
|
|
true
|
|
end
|
|
|
|
def valid_progress?
|
|
progress = row['Progress']
|
|
return true if progress.nil? || progress == '' || progress.downcase == 'n/a' || progress.downcase == 'na'
|
|
|
|
progress = progress.to_i
|
|
progress.to_i >= 25
|
|
end
|
|
|
|
def valid_grade?
|
|
return true if grade.nil?
|
|
|
|
return true if survey_type == :teacher
|
|
|
|
respondents = Respondent.where(school:, academic_year:).first
|
|
if respondents.present? && respondents.counts_by_grade[grade].present?
|
|
enrollment = respondents.counts_by_grade[grade]
|
|
end
|
|
return false if enrollment.nil?
|
|
|
|
valid = enrollment > 0
|
|
puts "Invalid grade #{grade} for #{school.name} #{academic_year.formatted_range}" unless valid
|
|
valid
|
|
end
|
|
|
|
def valid_sd?
|
|
return true if survey_type == :early_education
|
|
|
|
survey_item_headers = headers.filter(&:present?).filter { |header| header.start_with?('s-', 't-') }
|
|
likert_scores = []
|
|
survey_item_headers.each do |header|
|
|
likert_scores << likert_score(survey_item_id: header).to_i
|
|
end
|
|
likert_scores = likert_scores.compact.reject(&:zero?)
|
|
return false if likert_scores.count < 2
|
|
|
|
!likert_scores.stdev.zero?
|
|
end
|
|
|
|
def valid_school?
|
|
school.present?
|
|
end
|
|
|
|
private
|
|
|
|
def copy_likert_scores_from_variant_survey_items
|
|
headers.filter(&:present?).filter { |header| header.end_with? '-1' }.each do |header|
|
|
likert_score = row[header]
|
|
main_item = header.gsub('-1', '')
|
|
row[main_item] = likert_score if likert_score.present?
|
|
end
|
|
end
|
|
end
|
|
|
|
module RowMonkeyPatches
|
|
def remove_unwanted_columns
|
|
to_h.filter do |key, _value|
|
|
key.present?
|
|
end.reject { |key, _value| key.start_with? 'Q' }.reject { |key, _value| key.end_with? '-1' }.values
|
|
end
|
|
end
|
|
|
|
CSV::Row.include RowMonkeyPatches
|