Add disaggregation by ELL

rpp-main
rebuilt 2 years ago
parent 490522eb1e
commit 2fd56047d4

@ -28,7 +28,9 @@ export default class extends Controller {
"&incomes=" +
this.selected_items("income").join(",") +
"&grades=" +
this.selected_items("grade").join(",");
this.selected_items("grade").join(",") +
"&ells=" +
this.selected_items("ell").join(",");
this.go_to(url);
}
@ -126,7 +128,8 @@ export default class extends Controller {
['gender', 'students-by-gender'],
['grade', 'students-by-grade'],
['income', 'students-by-income'],
['race', 'students-by-race']
['race', 'students-by-race'],
['ell', 'students-by-ell'],
])
if (target.name === 'slice' || target.name === 'group') {

@ -16,6 +16,11 @@ class District < ApplicationRecord
end
def self.boston
District.find_by_name('Boston')
District.find_by_name("Boston")
end
def short_name
name.split(" ").first.downcase
end
end

@ -0,0 +1,7 @@
class Ell < ApplicationRecord
scope :by_designation, -> { all.map { |ell| [ell.designation, ell] }.to_h }
include FriendlyId
friendly_id :designation, use: [:slugged]
end

@ -1,5 +1,5 @@
class Gender < ApplicationRecord
scope :gender_hash, lambda {
scope :by_qualtrics_code, lambda {
all.map { |gender| [gender.qualtrics_code, gender] }.to_h
}
end

@ -1,5 +1,6 @@
class Income < ApplicationRecord
scope :by_designation, -> { all.map { |income| [income.designation, income] }.to_h }
scope :by_slug, -> { all.map { |income| [income.slug, income] }.to_h }
include FriendlyId

@ -19,4 +19,8 @@ class School < ApplicationRecord
.where(districts: { qualtrics_code: district_code })
.find_by_qualtrics_code(school_code)
end
def grades(academic_year:)
Respondent.find_by(school: self, academic_year:)&.counts_by_grade&.keys || (-1..12).to_a
end
end

@ -10,6 +10,7 @@ class SurveyItemResponse < ActiveRecord::Base
belongs_to :student, foreign_key: :student_id, optional: true
belongs_to :gender
belongs_to :income
belongs_to :ell
has_one :measure, through: :survey_item
@ -32,9 +33,15 @@ class SurveyItemResponse < ActiveRecord::Base
academic_year:, income:, grade: school.grades(academic_year:)).group(:survey_item).having("count(*) >= 10").average(:likert_score)
}
scope :averages_for_ell, lambda { |survey_items, school, academic_year, ell|
SurveyItemResponse.where(survey_item: survey_items, school:,
academic_year:, ell:, grade: school.grades(academic_year:)).group(:survey_item).having("count(*) >= 10").average(:likert_score)
}
scope :averages_for_race, lambda { |school, academic_year, race|
SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where(
school:, academic_year:, grade: school.grades(academic_year:)
).where("student_races.race_id": race.id).group(:survey_item_id).having("count(*) >= 10").average(:likert_score)
}
end

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Analyze
module Graph
module Column
module EllColumn
class Ell < GroupedBarColumnPresenter
include Analyze::Graph::Column::EllColumn::ScoreForEll
include Analyze::Graph::Column::EllColumn::EllCount
def label
%w[ELL]
end
def basis
"student"
end
def show_irrelevancy_message?
false
end
def show_insufficient_data_message?
false
end
def ell
::Ell.find_by_slug "ell"
end
end
end
end
end
end

@ -0,0 +1,18 @@
module Analyze
module Graph
module Column
module EllColumn
module EllCount
def type
:student
end
def n_size(year_index)
SurveyItemResponse.where(ell:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
academic_year: academic_years[year_index]).select(:response_id).distinct.count
end
end
end
end
end
end

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Analyze
module Graph
module Column
module EllColumn
class NotEll < GroupedBarColumnPresenter
include Analyze::Graph::Column::EllColumn::ScoreForEll
include Analyze::Graph::Column::EllColumn::EllCount
def label
%w[Not-ELL]
end
def basis
"student"
end
def show_irrelevancy_message?
false
end
def show_insufficient_data_message?
false
end
def ell
::Ell.find_by_slug "not-ell"
end
end
end
end
end
end

@ -0,0 +1,42 @@
module Analyze
module Graph
module Column
module EllColumn
module ScoreForEll
def score(year_index)
academic_year = academic_years[year_index]
meets_student_threshold = sufficient_student_responses?(academic_year:)
return Score::NIL_SCORE unless meets_student_threshold
averages = SurveyItemResponse.averages_for_ell(measure.student_survey_items, school, academic_year,
ell)
average = bubble_up_averages(averages:).round(2)
Score.new(average:,
meets_teacher_threshold: false,
meets_student_threshold:,
meets_admin_data_threshold: false)
end
def bubble_up_averages(averages:)
measure.student_scales.map do |scale|
scale.survey_items.map do |survey_item|
averages[survey_item]
end.remove_blanks.average
end.remove_blanks.average
end
def sufficient_student_responses?(academic_year:)
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
ell:, survey_item: measure.student_survey_items).group(:ell).select(:response_id).distinct(:response_id).count
yearly_counts.any? do |count|
count[1] >= 10
end
end
end
end
end
end
end

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Analyze
module Graph
module Column
module EllColumn
class Unknown < GroupedBarColumnPresenter
include Analyze::Graph::Column::EllColumn::ScoreForEll
include Analyze::Graph::Column::EllColumn::EllCount
def label
%w[Unknown]
end
def basis
"student"
end
def show_irrelevancy_message?
false
end
def show_insufficient_data_message?
false
end
def ell
::Ell.find_by_slug "unknown"
end
end
end
end
end
end

@ -0,0 +1,44 @@
# frozen_string_literal: true
#
module Analyze
module Graph
class StudentsByEll
include Analyze::Graph::Column::GenderColumn
attr_reader :ells
def initialize(ells:)
@ells = ells
end
def to_s
"Students by Ell"
end
def slug
"students-by-ell"
end
def columns
[].tap do |array|
ells.each do |ell|
array << column_for_ell_code(code: ell.slug)
end
array.sort_by!(&:to_s)
array << Analyze::Graph::Column::AllStudent
end
end
private
def column_for_ell_code(code:)
CFR[code]
end
CFR = {
"ell" => Analyze::Graph::Column::EllColumn::Ell,
"not-ell" => Analyze::Graph::Column::EllColumn::NotEll,
"unknown" => Analyze::Graph::Column::EllColumn::Unknown
}.freeze
end
end
end

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Analyze
module Graph
class StudentsByGender

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Analyze
module Graph
class StudentsByGrade
@ -9,11 +11,11 @@ module Analyze
end
def to_s
'Students by Grade'
"Students by Grade"
end
def slug
'students-by-grade'
"students-by-grade"
end
def columns

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Analyze
module Graph
class StudentsByIncome

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Analyze
module Graph
class StudentsByRace
@ -8,11 +10,11 @@ module Analyze
end
def to_s
'Students by Race'
"Students by Race"
end
def slug
'students-by-race'
"students-by-race"
end
def columns
@ -31,14 +33,14 @@ module Analyze
end
CFR = {
'1' => Analyze::Graph::Column::RaceColumn::AmericanIndian,
'2' => Analyze::Graph::Column::RaceColumn::Asian,
'3' => Analyze::Graph::Column::RaceColumn::Black,
'4' => Analyze::Graph::Column::RaceColumn::Hispanic,
'5' => Analyze::Graph::Column::RaceColumn::White,
'8' => Analyze::Graph::Column::RaceColumn::MiddleEastern,
'99' => Analyze::Graph::Column::RaceColumn::Unknown,
'100' => Analyze::Graph::Column::RaceColumn::Multiracial
"1" => Analyze::Graph::Column::RaceColumn::AmericanIndian,
"2" => Analyze::Graph::Column::RaceColumn::Asian,
"3" => Analyze::Graph::Column::RaceColumn::Black,
"4" => Analyze::Graph::Column::RaceColumn::Hispanic,
"5" => Analyze::Graph::Column::RaceColumn::White,
"8" => Analyze::Graph::Column::RaceColumn::MiddleEastern,
"99" => Analyze::Graph::Column::RaceColumn::Unknown,
"100" => Analyze::Graph::Column::RaceColumn::Multiracial
}.freeze
end
end

@ -0,0 +1,13 @@
module Analyze
module Group
class Ell
def name
"ELL"
end
def slug
"ell"
end
end
end
end

@ -54,9 +54,27 @@ module Analyze
end
end
def ells
@ells ||= Ell.all.order(slug: :ASC)
end
def selected_ells
@selected_ells ||= begin
ell_params = params[:ells]
return ells unless ell_params
ell_params.split(",").map { |ell| Ell.find_by_slug ell }.compact
end
end
def graphs
@graphs ||= [Analyze::Graph::AllData.new, Analyze::Graph::StudentsAndTeachers.new, Analyze::Graph::StudentsByRace.new(races: selected_races),
Analyze::Graph::StudentsByGrade.new(grades: selected_grades), Analyze::Graph::StudentsByGender.new(genders: selected_genders), Analyze::Graph::StudentsByIncome.new(incomes: selected_incomes)]
@graphs ||= [Analyze::Graph::AllData.new,
Analyze::Graph::StudentsAndTeachers.new,
Analyze::Graph::StudentsByRace.new(races: selected_races),
Analyze::Graph::StudentsByGrade.new(grades: selected_grades),
Analyze::Graph::StudentsByGender.new(genders: selected_genders),
Analyze::Graph::StudentsByIncome.new(incomes: selected_incomes),
Analyze::Graph::StudentsByEll.new(ells: selected_ells)]
end
def graph
@ -88,7 +106,7 @@ module Analyze
end
def groups
@groups = [Analyze::Group::Gender.new, Analyze::Group::Grade.new, Analyze::Group::Income.new,
@groups = [Analyze::Group::Ell.new, Analyze::Group::Gender.new, Analyze::Group::Grade.new, Analyze::Group::Income.new,
Analyze::Group::Race.new]
end

@ -1,6 +1,6 @@
require "fileutils"
class Cleaner
attr_reader :input_filepath, :output_filepath, :log_filepath, :clean_csv, :log_csv
attr_reader :input_filepath, :output_filepath, :log_filepath
def initialize(input_filepath:, output_filepath:, log_filepath:)
@input_filepath = input_filepath
@ -9,18 +9,13 @@ class Cleaner
initialize_directories
end
def initialize_directories
create_ouput_directory
create_log_directory
end
def clean
Dir.glob(Rails.root.join(input_filepath, "*.csv")).each do |filepath|
puts filepath
File.open(filepath) do |_file|
clean_csv = []
log_csv = []
data = []
File.open(filepath) do |file|
processed_data = process_raw_file(file:)
processed_data in [headers, clean_csv, log_csv, data]
return if data.empty?
filename = filename(headers:, data:)
write_csv(data: clean_csv, output_filepath:, filename:)
@ -37,23 +32,22 @@ class Cleaner
range = data.first.academic_year.range
districts = data.map do |row|
row.district.name
row.district.short_name
end.to_set.to_a
districts.join(".").to_s + "." + survey_type.to_s + "." + range + ".csv"
end
def process_raw_file(file:, disaggregation_data:)
def process_raw_file(file:)
clean_csv = []
log_csv = []
data = []
headers = (CSV.parse(file.first).first << "Raw Income") << "Income"
headers = CSV.parse(file.first).first.push("Raw Income").push("Income").push("Raw ELL").push("ELL").push("Raw SpEd").push("SpEd")
filtered_headers = include_all_headers(headers:)
filtered_headers = remove_unwanted_headers(headers: filtered_headers)
log_headers = (filtered_headers + ["Valid Duration?", "Valid Progress?", "Valid Grade?",
"Valid Standard Deviation?"]).flatten
clean_csv << filtered_headers
log_csv << log_headers
@ -62,7 +56,7 @@ class Cleaner
file.lazy.each_slice(1000) do |lines|
CSV.parse(lines.join, headers:).map do |row|
values = SurveyItemValues.new(row:, headers:, genders:,
survey_items: all_survey_items, schools:, disaggregation_data:)
survey_items: all_survey_items, schools:)
next unless values.valid_school?
data << values
@ -104,24 +98,12 @@ class Cleaner
File.write(output_filepath.join(prefix + filename), csv)
end
def process_row(row:)
clean_csv << row.to_csv
log_csv << row.to_csv
end
def schools
@schools ||= School.school_hash
end
def genders
@genders ||= begin
gender_hash = {}
Gender.all.each do |gender|
gender_hash[gender.qualtrics_code] = gender
end
gender_hash
end
@genders ||= Gender.by_qualtrics_code
end
def survey_items(headers:)
@ -138,8 +120,5 @@ class Cleaner
def create_log_directory
FileUtils.mkdir_p log_filepath
end
def create_file(path:, filename:)
FileUtils.touch path.join(filename)
end
end

@ -6,12 +6,13 @@ class DemographicLoader
process_race(row:)
process_gender(row:)
process_income(row:)
process_ell(row:)
end
end
def self.process_race(row:)
qualtrics_code = row['Race Qualtrics Code'].to_i
designation = row['Race/Ethnicity']
qualtrics_code = row["Race Qualtrics Code"].to_i
designation = row["Race/Ethnicity"]
return unless qualtrics_code && designation
if qualtrics_code.between?(6, 7)
@ -22,8 +23,8 @@ class DemographicLoader
end
def self.process_gender(row:)
qualtrics_code = row['Gender Qualtrics Code'].to_i
designation = row['Sex/Gender']
qualtrics_code = row["Gender Qualtrics Code"].to_i
designation = row["Sex/Gender"]
return unless qualtrics_code && designation
gender = ::Gender.find_or_create_by!(qualtrics_code:, designation:)
@ -31,11 +32,18 @@ class DemographicLoader
end
def self.process_income(row:)
designation = row['Income']
designation = row["Income"]
return unless designation
Income.find_or_create_by!(designation:)
end
def self.process_ell(row:)
designation = row["ELL"]
return unless designation
Ell.find_or_create_by!(designation:)
end
end
class KnownRace
@ -50,7 +58,7 @@ end
class UnknownRace
def initialize(qualtrics_code:, designation:)
unknown = Race.find_or_create_by!(qualtrics_code: 99)
unknown.designation = 'Race/Ethnicity Not Listed'
unknown.designation = "Race/Ethnicity Not Listed"
unknown.slug = designation.parameterize
unknown.save
end

@ -0,0 +1,54 @@
class DisaggregationRow
attr_reader :row, :headers
def initialize(row:, headers:)
@row = row
@headers = headers
end
def district
@district ||= value_from(pattern: /District/i)
end
def academic_year
@academic_year ||= value_from(pattern: /Academic\s*Year/i)
end
def raw_income
@income ||= value_from(pattern: /Low\s*Income/i)
end
def lasid
@lasid ||= value_from(pattern: /LASID/i)
end
def raw_ell
@raw_ell ||= value_from(pattern: /EL Student First Year/i)
end
def ell
@ell ||= begin
value = value_from(pattern: /EL Student First Year/i).downcase
case value
when /lep student 1st year|LEP student not 1st year/i
"ELL"
when /Does not apply/i
"Not ELL"
else
"Unknown"
end
end
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
end

@ -9,11 +9,12 @@ class SurveyItemValues
@genders = genders
@survey_items = survey_items
@schools = schools
@disaggregation_data = disaggregation_data
copy_likert_scores_from_variant_survey_items
row["Income"] = income
row["Raw Income"] = raw_income
row["Raw ELL"] = raw_ell
row["ELL"] = ell
copy_data_to_main_column(main: /Race/i, secondary: /Race Secondary|Race-1/i)
copy_data_to_main_column(main: /Gender/i, secondary: /Gender Secondary|Gender-1/i)
@ -134,20 +135,9 @@ class SurveyItemValues
def raw_income
@raw_income ||= value_from(pattern: /Low\s*Income|Raw\s*Income/i)
return @raw_income if @raw_income.present?
return "Unknown" unless disaggregation_data.present?
disaggregation = disaggregation_data[[lasid, district.name, academic_year.range]]
return "Unknown" unless disaggregation.present?
@raw_income ||= disaggregation.income
end
def income
@income ||= value_from(pattern: /^Income$/i)
return @income if @income.present?
@income ||= case raw_income
in /Free\s*Lunch|Reduced\s*Lunch|Low\s*Income/i
"Economically Disadvantaged - Y"
@ -158,6 +148,21 @@ class SurveyItemValues
end
end
def raw_ell
@raw_ell ||= value_from(pattern: /EL Student First Year|Raw\s*ELL/i)
end
def ell
@ell ||= case raw_ell
in /lep student 1st year|LEP student not 1st year|EL Student First Year/i
"ELL"
in /Does not apply/i
"Not ELL"
else
"Unknown"
end
end
def value_from(pattern:)
output = nil
matches = headers.select do |header|

@ -1,31 +1,25 @@
# frozen_string_literal: true
class SurveyResponsesDataLoader
def self.load_data(filepath:, rules: [Rule::NoRule])
def load_data(filepath:, rules: [Rule::NoRule])
File.open(filepath) do |file|
headers = file.first
headers_array = CSV.parse(headers).first
genders = Gender.gender_hash
schools = School.school_hash
incomes = Income.by_designation
all_survey_items = survey_items(headers:)
file.lazy.each_slice(500) do |lines|
survey_item_responses = CSV.parse(lines.join, headers:).map do |row|
process_row(row: SurveyItemValues.new(row:, headers: headers_array, genders:, survey_items: all_survey_items, schools:),
rules:, incomes:)
rules:)
end
SurveyItemResponse.import survey_item_responses.compact.flatten, batch_size: 500
end
end
end
def self.from_file(file:, rules: [])
def from_file(file:, rules: [])
headers = file.gets
headers_array = CSV.parse(headers).first
genders = Gender.gender_hash
schools = School.school_hash
incomes = Income.by_designation
all_survey_items = survey_items(headers:)
survey_item_responses = []
@ -36,7 +30,7 @@ class SurveyResponsesDataLoader
CSV.parse(line, headers:).map do |row|
survey_item_responses << process_row(row: SurveyItemValues.new(row:, headers: headers_array, genders:, survey_items: all_survey_items, schools:),
rules:, incomes:)
rules:)
end
row_count += 1
@ -52,7 +46,23 @@ class SurveyResponsesDataLoader
private
def self.process_row(row:, rules:, incomes:)
def schools
@schools = School.school_hash
end
def genders
@genders = Gender.by_qualtrics_code
end
def incomes
@incomes ||= Income.by_slug
end
def ells
@ells ||= Ell.by_designation
end
def process_row(row:, rules:)
return unless row.dese_id?
return unless row.school.present?
@ -60,10 +70,10 @@ class SurveyResponsesDataLoader
return if rule.new(row:).skip_row?
end
process_survey_items(row:, incomes:)
process_survey_items(row:)
end
def self.process_survey_items(row:, incomes:)
def process_survey_items(row:)
row.survey_items.map do |survey_item|
likert_score = row.likert_score(survey_item_id: survey_item.survey_item_id) || next
@ -72,38 +82,33 @@ class SurveyResponsesDataLoader
next
end
response = row.survey_item_response(survey_item:)
create_or_update_response(survey_item_response: response, likert_score:, row:, survey_item:, incomes:)
create_or_update_response(survey_item_response: response, likert_score:, row:, survey_item:)
end.compact
end
def self.create_or_update_response(survey_item_response:, likert_score:, row:, survey_item:, incomes:)
def create_or_update_response(survey_item_response:, likert_score:, row:, survey_item:)
gender = row.gender
grade = row.grade
income = incomes[row.income]
income = incomes[row.income.parameterize]
ell = ells[row.ell]
if survey_item_response.present?
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:)
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:)
[]
else
SurveyItemResponse.new(response_id: row.response_id, academic_year: row.academic_year, school: row.school, survey_item:,
likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:)
likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:)
end
end
def self.survey_items(headers:)
def survey_items(headers:)
SurveyItem.where(survey_item_id: get_survey_item_ids_from_headers(headers:))
end
def self.get_survey_item_ids_from_headers(headers:)
def get_survey_item_ids_from_headers(headers:)
CSV.parse(headers).first
.filter(&:present?)
.filter { |header| header.start_with? "t-", "s-" }
end
private_class_method :process_row
private_class_method :process_survey_items
private_class_method :create_or_update_response
private_class_method :survey_items
private_class_method :get_survey_item_ids_from_headers
end
module StringMonkeyPatches

@ -21,3 +21,7 @@
<% @presenter.incomes.each do |income| %>
<%= render(partial: "checkboxes", locals: {id: "income-#{income.slug}", item: income, selected_items: @presenter.selected_incomes, name: "income", label_text: income.designation}) %>
<% end %>
<% @presenter.ells.each do |ell| %>
<%= render(partial: "checkboxes", locals: {id: "ell-#{ell.slug}", item: ell, selected_items: @presenter.selected_ells, name: "ell", label_text: ell.designation}) %>
<% end %>

@ -13,7 +13,7 @@
<%= render partial: "school_years", locals: {available_academic_years: @presenter.academic_years, selected_academic_years: @presenter.selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory, measures: @presenter.measures, graph: @presenter.graph} %>
<%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory} %>
</div>
<% cache [@presenter.subcategory, @school, @presenter.selected_academic_years, @presenter.graph, @presenter.selected_races, @presenter.race_score_timestamp, @presenter.selected_grades, @presenter.grades, @presenter.selected_genders, @presenter.genders] do %>
<% cache [@presenter.subcategory, @school, @presenter.selected_academic_years, @presenter.graph, @presenter.selected_races, @presenter.selected_grades, @presenter.grades, @presenter.selected_genders, @presenter.genders, @presenter.selected_ells, @presenter.ells] do %>
<div class="bg-color-white flex-grow-1 col-9">
<% @presenter.measures.each do |measure| %>
<section class="mb-6">

@ -1,11 +1,11 @@
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged - N
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged - Y
3,Black or African American,4,Non-Binary,Unknown
4,Hispanic or Latinx,99,Unknown,
5,White or Caucasian,,,
6,Prefer not to disclose,,,
7,Prefer to self-describe,,,
8,Middle Eastern,,,
99,Race/Ethnicity Not Listed,,,
100,Multiracial,,,
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income,ELL
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged - N,ELL
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged - Y,Not ELL
3,Black or African American,4,Non-Binary,Unknown,Unknown
4,Hispanic or Latinx,99,Unknown,,
5,White or Caucasian,,,,
6,Prefer not to disclose,,,,
7,Prefer to self-describe,,,,
8,Middle Eastern,,,,
99,Race/Ethnicity Not Listed,,,,
100,Multiracial,,,,

1 Race Qualtrics Code Race/Ethnicity Gender Qualtrics Code Sex/Gender Income ELL
2 1 American Indian or Alaskan Native 2 Male Economically Disadvantaged - N ELL
3 2 Asian or Pacific Islander 1 Female Economically Disadvantaged - Y Not ELL
4 3 Black or African American 4 Non-Binary Unknown Unknown
5 4 Hispanic or Latinx 99 Unknown
6 5 White or Caucasian
7 6 Prefer not to disclose
8 7 Prefer to self-describe
9 8 Middle Eastern
10 99 Race/Ethnicity Not Listed
11 100 Multiracial

@ -0,0 +1,12 @@
class CreateEll < ActiveRecord::Migration[7.0]
def change
create_table :ells do |t|
t.string :designation
t.string :slug
t.timestamps
end
add_index :ells, :designation, unique: true
end
end

@ -0,0 +1,5 @@
class AddEllToSurveyItemResponse < ActiveRecord::Migration[7.0]
def change
add_reference :survey_item_responses, :ell, foreign_key: true
end
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
ActiveRecord::Schema[7.0].define(version: 2023_09_12_223701) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -69,6 +69,14 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
t.datetime "updated_at", null: false
end
create_table "ells", force: :cascade do |t|
t.string "designation"
t.string "slug"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["designation"], name: "index_ells_on_designation", unique: true
end
create_table "genders", force: :cascade do |t|
t.integer "qualtrics_code"
t.string "designation"
@ -352,7 +360,7 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
t.integer "eleven"
t.integer "twelve"
t.index ["academic_year_id"], name: "index_respondents_on_academic_year_id"
t.index %w[school_id academic_year_id], name: "index_respondents_on_school_id_and_academic_year_id", unique: true
t.index ["school_id", "academic_year_id"], name: "index_respondents_on_school_id_and_academic_year_id", unique: true
end
create_table "response_rates", force: :cascade do |t|
@ -366,7 +374,7 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["academic_year_id"], name: "index_response_rates_on_academic_year_id"
t.index %w[school_id subcategory_id], name: "index_response_rates_on_school_id_and_subcategory_id"
t.index ["school_id", "subcategory_id"], name: "index_response_rates_on_school_id_and_subcategory_id"
t.index ["school_id"], name: "index_response_rates_on_school_id"
t.index ["subcategory_id"], name: "index_response_rates_on_subcategory_id"
end
@ -419,7 +427,7 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["race_id"], name: "index_student_races_on_race_id"
t.index %w[student_id race_id], name: "index_student_races_on_student_id_and_race_id"
t.index ["student_id", "race_id"], name: "index_student_races_on_student_id_and_race_id"
t.index ["student_id"], name: "index_student_races_on_student_id"
end
@ -453,12 +461,16 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
t.integer "grade"
t.bigint "gender_id"
t.datetime "recorded_date"
t.bigint "ell_id"
t.bigint "income_id"
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
t.index ["ell_id"], name: "index_survey_item_responses_on_ell_id"
t.index ["gender_id"], name: "index_survey_item_responses_on_gender_id"
t.index ["income_id"], name: "index_survey_item_responses_on_income_id"
t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
t.index %w[school_id academic_year_id survey_item_id], name: "by_school_year_and_survey_item"
t.index %w[school_id academic_year_id], name: "index_survey_item_responses_on_school_id_and_academic_year_id"
t.index %w[school_id survey_item_id academic_year_id grade], name: "index_survey_responses_on_grade"
t.index ["school_id", "academic_year_id", "survey_item_id"], name: "by_school_year_and_survey_item"
t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_id"
t.index ["school_id", "survey_item_id", "academic_year_id", "grade"], name: "index_survey_responses_on_grade"
t.index ["school_id"], name: "index_survey_item_responses_on_school_id"
t.index ["student_id"], name: "index_survey_item_responses_on_student_id"
t.index ["survey_item_id"], name: "index_survey_item_responses_on_survey_item_id"
@ -508,7 +520,9 @@ ActiveRecord::Schema[7.0].define(version: 20_230_630_215_110) do
add_foreign_key "student_races", "students"
add_foreign_key "subcategories", "categories"
add_foreign_key "survey_item_responses", "academic_years"
add_foreign_key "survey_item_responses", "ells"
add_foreign_key "survey_item_responses", "genders"
add_foreign_key "survey_item_responses", "incomes"
add_foreign_key "survey_item_responses", "schools"
add_foreign_key "survey_item_responses", "students"
add_foreign_key "survey_item_responses", "survey_items"

@ -1,33 +1,33 @@
namespace :clean do
# These tasks must be run in their respective project so the correct schools are in the database
desc 'clean ecp data'
desc "clean ecp data"
task ecp: :environment do
input_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'raw')
output_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'clean')
log_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'removed')
input_filepath = Rails.root.join("tmp", "data", "ecp_data", "raw")
output_filepath = Rails.root.join("tmp", "data", "ecp_data", "clean")
log_filepath = Rails.root.join("tmp", "data", "ecp_data", "removed")
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
end
desc 'clean prepped data'
desc "clean prepped data"
task prepped: :environment do
input_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'prepped')
output_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'prepped', 'clean')
log_filepath = Rails.root.join('tmp', 'data', 'ecp_data', 'prepped', 'removed')
input_filepath = Rails.root.join("tmp", "data", "ecp_data", "prepped")
output_filepath = Rails.root.join("tmp", "data", "ecp_data", "prepped", "clean")
log_filepath = Rails.root.join("tmp", "data", "ecp_data", "prepped", "removed")
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
end
desc 'clean mciea data'
desc "clean mciea data"
task mciea: :environment do
input_filepath = Rails.root.join('tmp', 'data', 'mciea_data', 'raw')
output_filepath = Rails.root.join('tmp', 'data', 'mciea_data', 'clean')
log_filepath = Rails.root.join('tmp', 'data', 'mciea_data', 'removed')
input_filepath = Rails.root.join("tmp", "data", "mciea_data", "raw")
output_filepath = Rails.root.join("tmp", "data", "mciea_data", "clean")
log_filepath = Rails.root.join("tmp", "data", "mciea_data", "removed")
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
end
desc 'clean rpp data'
desc "clean rpp data"
task rpp: :environment do
input_filepath = Rails.root.join('tmp', 'data', 'rpp_data', 'raw')
output_filepath = Rails.root.join('tmp', 'data', 'rpp_data', 'clean')
log_filepath = Rails.root.join('tmp', 'data', 'rpp_data', 'removed')
input_filepath = Rails.root.join("tmp", "data", "rpp_data", "raw")
output_filepath = Rails.root.join("tmp", "data", "rpp_data", "clean")
log_filepath = Rails.root.join("tmp", "data", "rpp_data", "removed")
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
end
end

@ -29,7 +29,7 @@ namespace :data do
student_count = Student.count
path = '/data/survey_responses/clean/'
Sftp::Directory.open(path:) do |file|
SurveyResponsesDataLoader.from_file(file:)
SurveyResponsesDataLoader.new.from_file(file:)
end
puts "=====================> Completed loading #{SurveyItemResponse.count - survey_item_response_count} survey responses. #{SurveyItemResponse.count} total responses in the database"

@ -82,27 +82,6 @@ namespace :one_off do
puts values
end
desc "load survey responses for lowell schools"
task load_survey_responses_for_lowell: :environment do
survey_item_response_count = SurveyItemResponse.count
student_count = Student.count
Sftp::Directory.open(path: "/test/survey_responses/") do |file|
SurveyResponsesDataLoader.from_file(file:)
end
puts "=====================> Completed loading #{SurveyItemResponse.count - survey_item_response_count} survey responses. #{SurveyItemResponse.count} total responses in the database"
Sftp::Directory.open(path: "/test/survey_responses/") do |file|
StudentLoader.from_file(file:, rules: [Rule::SkipNonLowellSchools])
end
puts "=====================> Completed loading #{Student.count - student_count} students. #{Student.count} total students"
puts "Resetting race scores"
RaceScoreLoader.reset(fast_processing: false)
puts "=====================> Completed loading #{RaceScore.count} race scores"
Rails.cache.clear
end
desc "delete 2022-23 survey responses"
task delete_survey_responses_2022_23: :environment do
response_count = SurveyItemResponse.all.count
@ -127,7 +106,7 @@ namespace :one_off do
path = "/data/survey_responses/2022-23/"
Sftp::Directory.open(path:) do |file|
SurveyResponsesDataLoader.from_file(file:)
SurveyResponsesDataLoader.new.from_file(file:)
end
puts "=====================> Completed loading #{SurveyItemResponse.count - survey_item_response_count} survey responses. #{SurveyItemResponse.count} total responses in the database"

@ -1,11 +1,15 @@
FactoryBot.define do
factory :income do
designation { "MyString" }
designation { "DefaultIncome" }
end
factory :ell do
designation { "DefaultEll" }
end
factory :gender do
qualtrics_code { 1 }
designation { 'MyString' }
designation { "MyString" }
end
factory :race_score do
@ -68,22 +72,22 @@ FactoryBot.define do
end
factory :academic_year do
range { '2050-51' }
range { "2050-51" }
initialize_with { AcademicYear.find_or_initialize_by(range:) }
end
factory :category, class: 'Category' do
factory :category, class: "Category" do
name { "A #{rand} category" }
category_id { rand.to_s }
description { 'A description of a category' }
description { "A description of a category" }
slug { name.parameterize }
sort_index { 1 }
end
factory :subcategory do
name { 'A subcategory' }
name { "A subcategory" }
subcategory_id { rand.to_s }
description { 'A description of a subcategory' }
description { "A description of a subcategory" }
category
factory :subcategory_with_measures do
@ -102,7 +106,7 @@ FactoryBot.define do
factory :measure do
measure_id { rand.to_s }
name { 'A Measure' }
name { "A Measure" }
subcategory
trait :with_student_survey_items do
after(:create) do |measure|
@ -136,7 +140,7 @@ FactoryBot.define do
factory :survey_item do
scale
prompt { 'What do YOU think?' }
prompt { "What do YOU think?" }
factory :teacher_survey_item do
survey_item_id { "t-#{rand}" }
watch_low_benchmark { 2.0 }

@ -1,11 +1,11 @@
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged N
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged Y
3,Black or African American,4,Non-Binary,Unknown
4,Hispanic or Latinx,99,Unknown,
5,White or Caucasian,,,
6,Prefer not to disclose,,,
7,Prefer to self-describe,,,
8,Middle Eastern,,,
99,Race/Ethnicity Not Listed,,,
100,Multiracial,,,
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income,ELL
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged N,ELL
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged Y,Not ELL
3,Black or African American,4,Non-Binary,Unknown,Unknown
4,Hispanic or Latinx,99,Unknown,,
5,White or Caucasian,,,,
6,Prefer not to disclose,,,,
7,Prefer to self-describe,,,,
8,Middle Eastern,,,,
99,Race/Ethnicity Not Listed,,,,
100,Multiracial,,,,

1 Race Qualtrics Code Race/Ethnicity Gender Qualtrics Code Sex/Gender Income ELL
2 1 American Indian or Alaskan Native 2 Male Economically Disadvantaged – N ELL
3 2 Asian or Pacific Islander 1 Female Economically Disadvantaged – Y Not ELL
4 3 Black or African American 4 Non-Binary Unknown Unknown
5 4 Hispanic or Latinx 99 Unknown
6 5 White or Caucasian
7 6 Prefer not to disclose
8 7 Prefer to self-describe
9 8 Middle Eastern
10 99 Race/Ethnicity Not Listed
11 100 Multiracial

@ -1,9 +1,8 @@
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LASID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,district,school,DESE ID,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-tint-q1,s-tint-q2,#N/A,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,#N/A,#N/A,s-cure-q3,s-cure-q4,#N/A,s-sten-q2,s-sten-q3,s-sper-q1,s-sper-q2,s-sper-q3,s-sper-q4,s-civp-q1,s-civp-q2,s-civp-q3,s-civp-q4,s-grmi-q1,#N/A,#N/A,s-grmi-q4,s-appa-q1,s-appa-q2,#N/A,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,s-phys-q1-1,s-phys-q2,s-phys-q3,s-phys-q4,s-vale-q1,s-vale-q2,s-vale-q3,s-vale-q4,s-acst-q1,s-acst-q2,s-acst-q3,s-acst-q4,s-acst-q5,s-grit-q1,s-grit-q2,s-grit-q3,s-grit-q4,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,race,What is your race/ethnicity?(Please select all that apply) - Selected Choice,grade,gender,Raw Income,Income
2020-09-29 18:28:41,2020-09-29 18:48:28,0,73.249.89.226,6,1186,0,2020-09-30T18:48:50,student_survey_response_1,123456,,,,,,,anonymous,EN,1,8,1500025,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,,1,888,11th,1,Free Lunch,Economically Disadvantaged Y
2021-02-23 15:12:58,2021-02-23 15:13:17,0,50.207.254.114,0,19,0,2021-02-24T15:13:19,student_survey_response_2,234567,,,,,,,anonymous,EN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,NA,,,,,,,,,,,,,,,,,,,,,EN,,,,,"2,3,4",888,10,,Not Eligible,Economically Disadvantaged N
2021-03-31 9:50:19,2021-03-31 9:59:01,0,108.7.17.250,100,522,1,2021-03-31T09:59:02,student_survey_response_3,345678,,,,,42.53340149,-70.96530151,anonymous,EN,3,2,1500505,12,4,108,3300,7,1,,,,,,,,,,,,,,2,4,2,1,4,3,3,,,,,3,3,3,3,,,,,NA,,,,,,,,,3,2,3,3,2,1,3,3,4,1,3,3,4,4,2,4,3,3,4,3,3,3,4,3,3,3,3,3,,,,,,,,,,3,4,4,2,3,3,1,,3,,EN,Math teacher,,,,6,888,8,2,Reduced Lunch,Economically Disadvantaged Y
2021-03-31 9:50:09,2021-03-31 10:00:16,0,67.186.188.168,100,607,1,2021-03-31T10:00:17,student_survey_response_4,456789,,,,,42.63510132,-71.30139923,anonymous,EN,3,2,1500505,12,18,108,2064,7,1,,2,2,1,,,,,,,,,,,,,,,,,,,,,,,,,3,5,3,3,,,,,,,,,,4,4,3,4,5,1,,1,5,1,3,2,4,4,1,2,1,3,2,3,3,3,4,2,5,3,4,5,5,3,3,4,3,,,,,4,4,4,4,3,5,2,,2,,EN,,,,English teacher,7,888,8,3,,Unknown
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_5,567890,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8,6,7",888,7,4,Free Lunch,Economically Disadvantaged Y
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_6,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8",888,3,NA,Not Eligible,Economically Disadvantaged N
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_7,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,,,4,,Reduced Lunch,Economically Disadvantaged Y
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"1,2,3,4,5,8",,,,,
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LASID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,district,school,DESE ID,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-tint-q1,s-tint-q2,#N/A,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,#N/A,#N/A,s-cure-q3,s-cure-q4,#N/A,s-sten-q2,s-sten-q3,s-sper-q1,s-sper-q2,s-sper-q3,s-sper-q4,s-civp-q1,s-civp-q2,s-civp-q3,s-civp-q4,s-grmi-q1,#N/A,#N/A,s-grmi-q4,s-appa-q1,s-appa-q2,#N/A,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,s-phys-q1-1,s-phys-q2,s-phys-q3,s-phys-q4,s-vale-q1,s-vale-q2,s-vale-q3,s-vale-q4,s-acst-q1,s-acst-q2,s-acst-q3,s-acst-q4,s-acst-q5,s-grit-q1,s-grit-q2,s-grit-q3,s-grit-q4,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,race,What is your race/ethnicity?(Please select all that apply) - Selected Choice,grade,gender,Raw Income,Income,Raw ELL,ELL
2020-09-29 18:28:41,2020-09-29 18:48:28,0,73.249.89.226,6,1186,0,2020-09-30T18:48:50,student_survey_response_1,123456,,,,,,,anonymous,EN,1,8,1500025,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,,1,888,11th,1,Free Lunch,Economically Disadvantaged Y,Does not apply,Not ELL
2021-02-23 15:12:58,2021-02-23 15:13:17,0,50.207.254.114,0,19,0,2021-02-24T15:13:19,student_survey_response_2,234567,,,,,,,anonymous,EN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,NA,,,,,,,,,,,,,,,,,,,,,EN,,,,,"2,3,4",888,10,,Not Eligible,Economically Disadvantaged N,,Unknown
2021-03-31 9:50:19,2021-03-31 9:59:01,0,108.7.17.250,100,522,1,2021-03-31T09:59:02,student_survey_response_3,345678,,,,,42.53340149,-70.96530151,anonymous,EN,3,2,1500505,12,4,108,3300,7,1,,,,,,,,,,,,,,2,4,2,1,4,3,3,,,,,3,3,3,3,,,,,NA,,,,,,,,,3,2,3,3,2,1,3,3,4,1,3,3,4,4,2,4,3,3,4,3,3,3,4,3,3,3,3,3,,,,,,,,,,3,4,4,2,3,3,1,,3,,EN,Math teacher,,,,6,888,8,2,Reduced Lunch,Economically Disadvantaged Y,,Unknown
2021-03-31 9:50:09,2021-03-31 10:00:16,0,67.186.188.168,100,607,1,2021-03-31T10:00:17,student_survey_response_4,456789,,,,,42.63510132,-71.30139923,anonymous,EN,3,2,1500505,12,18,108,2064,7,1,,2,2,1,,,,,,,,,,,,,,,,,,,,,,,,,3,5,3,3,,,,,,,,,,4,4,3,4,5,1,,1,5,1,3,2,4,4,1,2,1,3,2,3,3,3,4,2,5,3,4,5,5,3,3,4,3,,,,,4,4,4,4,3,5,2,,2,,EN,,,,English teacher,7,888,8,3,,Unknown,LEP student not 1st year,ELL
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_5,567890,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8,6,7",888,7,4,Free Lunch,Economically Disadvantaged Y,EL Student First Year,ELL
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_6,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8",888,3,NA,Not Eligible,Economically Disadvantaged N,Unknown,Unknown
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_7,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,,,4,,Reduced Lunch,Economically Disadvantaged Y,#N/A,Unknown

1 Start Date End Date Response Type IP Address Progress Duration (in seconds) Finished RecordedDate ResponseId LASID Recipient Last Name Recipient First Name Recipient Email External Data Reference Location Latitude Location Longitude Distribution Channel User Language district school DESE ID #N/A #N/A #N/A #N/A #N/A #N/A #N/A s-emsa-q1 s-emsa-q2 s-emsa-q3 s-tint-q1 s-tint-q2 #N/A s-tint-q4 s-tint-q5 s-acpr-q1 s-acpr-q2 s-acpr-q3 s-acpr-q4 #N/A #N/A s-cure-q3 s-cure-q4 #N/A s-sten-q2 s-sten-q3 s-sper-q1 s-sper-q2 s-sper-q3 s-sper-q4 s-civp-q1 s-civp-q2 s-civp-q3 s-civp-q4 s-grmi-q1 #N/A #N/A s-grmi-q4 s-appa-q1 s-appa-q2 #N/A s-peff-q1 s-peff-q2 s-peff-q3 s-peff-q4 s-peff-q5 s-peff-q6 s-sbel-q1 s-sbel-q2 s-sbel-q3 s-sbel-q4 s-sbel-q5 s-phys-q1 s-phys-q1-1 s-phys-q2 s-phys-q3 s-phys-q4 s-vale-q1 s-vale-q2 s-vale-q3 s-vale-q4 s-acst-q1 s-acst-q2 s-acst-q3 s-acst-q4 s-acst-q5 s-grit-q1 s-grit-q2 s-grit-q3 s-grit-q4 #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A #N/A race What is your race/ethnicity?(Please select all that apply) - Selected Choice grade gender Raw Income Income Raw ELL ELL
2 2020-09-29 18:28:41 2020-09-29 18:48:28 0 73.249.89.226 6 1186 0 2020-09-30T18:48:50 student_survey_response_1 123456 anonymous EN 1 8 1500025 dddd 4 3 3 0 some non-integer response 6 5 1 EN 1 888 11th 1 Free Lunch Economically Disadvantaged – Y Does not apply Not ELL
3 2021-02-23 15:12:58 2021-02-23 15:13:17 0 50.207.254.114 0 19 0 2021-02-24T15:13:19 student_survey_response_2 234567 anonymous EN NA EN 2,3,4 888 10 Not Eligible Economically Disadvantaged – N Unknown
4 2021-03-31 9:50:19 2021-03-31 9:59:01 0 108.7.17.250 100 522 1 2021-03-31T09:59:02 student_survey_response_3 345678 42.53340149 -70.96530151 anonymous EN 3 2 1500505 12 4 108 3300 7 1 2 4 2 1 4 3 3 3 3 3 3 NA 3 2 3 3 2 1 3 3 4 1 3 3 4 4 2 4 3 3 4 3 3 3 4 3 3 3 3 3 3 4 4 2 3 3 1 3 EN Math teacher 6 888 8 2 Reduced Lunch Economically Disadvantaged – Y Unknown
5 2021-03-31 9:50:09 2021-03-31 10:00:16 0 67.186.188.168 100 607 1 2021-03-31T10:00:17 student_survey_response_4 456789 42.63510132 -71.30139923 anonymous EN 3 2 1500505 12 18 108 2064 7 1 2 2 1 3 5 3 3 4 4 3 4 5 1 1 5 1 3 2 4 4 1 2 1 3 2 3 3 3 4 2 5 3 4 5 5 3 3 4 3 4 4 4 4 3 5 2 2 EN English teacher 7 888 8 3 Unknown LEP student not 1st year ELL
6 2021-03-31 9:51:39 2021-03-31 10:01:36 0 73.47.153.77 100 596 1 2021-03-31T10:01:36 student_survey_response_5 567890 42.65820313 -71.30580139 anonymous EN 3 2 1500505 6 15 109 3710 7 1 2 2 2 3 3 4 3 3 3 3 4 3 4 3 4 4 5 4 3 4 3 5 2 2 3 1 2 5 1 3 3 2 4 3 5 4 5 4 3 4 4 4 4 4 4 2 2 EN Social Studies teacher 1,2,3,4,5,8,6,7 888 7 4 Free Lunch Economically Disadvantaged – Y EL Student First Year ELL
7 2021-03-31 9:51:39 2021-03-31 10:01:36 0 73.47.153.77 100 596 1 2021-03-31T10:01:36 student_survey_response_6 42.65820313 -71.30580139 anonymous EN 3 2 1500505 6 15 109 3710 7 1 2 2 2 3 3 4 3 3 3 3 4 3 4 3 4 4 5 4 3 4 3 5 2 2 3 1 2 5 1 3 3 2 4 3 5 4 5 4 3 4 4 4 4 4 4 2 2 EN Social Studies teacher 1,2,3,4,5,8 888 3 NA Not Eligible Economically Disadvantaged – N Unknown Unknown
8 2021-03-31 9:51:39 2021-03-31 10:01:36 0 73.47.153.77 100 596 1 2021-03-31T10:01:36 student_survey_response_7 42.65820313 -71.30580139 anonymous EN 3 2 1500505 6 15 109 3710 7 1 2 2 2 3 3 4 3 3 3 3 4 3 4 3 4 4 5 4 3 4 3 5 2 2 3 1 2 5 1 3 3 2 4 3 5 4 5 4 3 4 4 4 4 4 4 2 2 EN Social Studies teacher 4 Reduced Lunch Economically Disadvantaged – Y #N/A Unknown
1,2,3,4,5,8

@ -2,13 +2,18 @@ require "rails_helper"
require "fileutils"
RSpec.describe Cleaner do
let(:district) { create(:district, name: "District1") }
let(:district) { create(:district, name: "Maynard Public Schools") }
let(:second_district) { create(:district, name: "District2") }
let(:school) { create(:school, dese_id: 1_740_505, district:) }
let(:second_school) { create(:school, dese_id: 222_222, district: second_district) }
let(:second_school) { create(:school, dese_id: 1_740_305, district:) }
let(:third_school) { create(:school, dese_id: 222_222, district: second_district) }
let(:academic_year) { create(:academic_year, range: "2022-23") }
let(:respondents) { create(:respondent, school:, academic_year:, nine: 40, ten: 40, eleven: 40, twelve: 40) }
let(:respondents) do
create(:respondent, school:, academic_year:, one: 0, nine: 40, ten: 40, eleven: 40, twelve: 40)
create(:respondent, school: second_school, academic_year:, one: 0, four: 40, five: 40, six: 40, seven: 40,
eight: 40)
end
let(:recorded_date) { "2023-04-01" }
let(:input_filepath) do
Rails.root.join("spec", "fixtures", "raw")
@ -22,6 +27,10 @@ RSpec.describe Cleaner do
Rails.root.join("tmp", "spec", "removed")
end
let(:path_to_sample_raw_file) do
File.open(Rails.root.join("spec", "fixtures", "raw", "sample_maynard_raw_student_survey.csv"))
end
let(:common_headers) do
["Recorded Date", "Dese ID", "ResponseID"]
end
@ -71,6 +80,7 @@ RSpec.describe Cleaner do
before :each do
school
second_school
third_school
standard_survey_items
short_form_survey_items
early_education_survey_items
@ -93,9 +103,9 @@ RSpec.describe Cleaner do
context ".process_raw_file" do
it "sorts data into valid and invalid csvs" do
cleaner = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:)
cleaner = Cleaner.new(input_filepath:, output_filepath:, log_filepath:)
processed_data = cleaner.process_raw_file(
file: path_to_sample_raw_file, disaggregation_data: cleaner.disaggregation_data
file: path_to_sample_raw_file
)
processed_data in [headers, clean_csv, log_csv, data]
@ -122,22 +132,6 @@ RSpec.describe Cleaner do
csv_contains_the_correct_rows(log_csv, invalid_rows)
invalid_rows_are_rejected_for_the_correct_reasons(data)
end
it "adds dissaggregation data to the cleaned file " do
cleaner = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:)
processed_data = cleaner.process_raw_file(
file: path_to_sample_raw_file, disaggregation_data: cleaner.disaggregation_data
)
processed_data in [headers, clean_csv, log_csv, data]
index_of_income = clean_csv.first.index("Income")
expect(clean_csv.second[index_of_income]).to eq "Economically Disadvantaged - Y"
one_thousand = data.find { |row| row.response_id == "1000" }
expect(one_thousand.income).to eq "Economically Disadvantaged - Y"
one_thousand_one = data.find { |row| row.response_id == "1001" }
expect(one_thousand_one.income).to eq "Economically Disadvantaged - N"
end
end
context ".filename" do
@ -151,7 +145,7 @@ RSpec.describe Cleaner do
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
headers: standard_survey_items, data:
)
expect(filename).to eq "District1.standard.2022-23.csv"
expect(filename).to eq "maynard.standard.2022-23.csv"
end
context "when the file is based on short form survey items" do
@ -163,7 +157,7 @@ RSpec.describe Cleaner do
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
headers: short_form_survey_items, data:
)
expect(filename).to eq "District1.short_form.2022-23.csv"
expect(filename).to eq "maynard.short_form.2022-23.csv"
end
end
@ -176,7 +170,7 @@ RSpec.describe Cleaner do
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
headers: early_education_survey_items, data:
)
expect(filename).to eq "District1.early_education.2022-23.csv"
expect(filename).to eq "maynard.early_education.2022-23.csv"
end
end
context "when the file is based on teacher survey items" do
@ -188,7 +182,7 @@ RSpec.describe Cleaner do
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
headers: teacher_survey_items, data:
)
expect(filename).to eq "District1.teacher.2022-23.csv"
expect(filename).to eq "maynard.teacher.2022-23.csv"
end
end
@ -202,7 +196,7 @@ RSpec.describe Cleaner do
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
headers: teacher_survey_items, data:
)
expect(filename).to eq "District1.District2.teacher.2022-23.csv"
expect(filename).to eq "maynard.district2.teacher.2022-23.csv"
end
end
end
@ -212,7 +206,7 @@ end
def reads_headers_from_raw_csv(processed_data)
processed_data in [headers, clean_csv, log_csv, data]
expect(headers.to_set.sort).to eq ["StartDate", "EndDate", "Status", "IPAddress", "Progress", "Duration (in seconds)",
expect(headers.to_set.sort).to eq ["StartDate", "EndDate", "Status", "IPAddress", "Progress", "Duration (in seconds)",
"Finished", "RecordedDate", "ResponseId", "District", "School",
"LASID", "Gender", "Race", "What grade are you in?", "s-emsa-q1", "s-emsa-q2", "s-emsa-q3", "s-tint-q1",
"s-tint-q2", "s-tint-q3", "s-tint-q4", "s-tint-q5", "s-acpr-q1", "s-acpr-q2",
@ -225,7 +219,7 @@ def reads_headers_from_raw_csv(processed_data)
"s-grit-q1", "s-grit-q2", "s-grit-q3", "s-grit-q4", "s-expa-q1", "s-poaf-q1", "s-poaf-q2", "s-poaf-q3",
"s-poaf-q4", "s-tint-q1-1", "s-tint-q2-1", "s-tint-q3-1", "s-tint-q4-1", "s-tint-q5-1", "s-acpr-q1-1",
"s-acpr-q2-1", "s-acpr-q3-1", "s-acpr-q4-1", "s-peff-q1-1", "s-peff-q2-1", "s-peff-q3-1", "s-peff-q4-1",
"s-peff-q5-1", "s-peff-q6-1", "Raw Income", "Income"].to_set.sort
"s-peff-q5-1", "s-peff-q6-1", "Raw Income", "Income", "Raw ELL", "ELL", "Raw SpEd", "SpEd"].to_set.sort
end
def invalid_rows_are_rejected_for_the_correct_reasons(data)
@ -308,3 +302,4 @@ def csv_contains_the_correct_rows(csv, rows)
expect(csv[index + 1][response_id]).to eq row
end
end

@ -1,20 +1,24 @@
require 'rails_helper'
require "rails_helper"
describe DemographicLoader do
let(:filepath) { 'spec/fixtures/sample_demographics.csv' }
let(:filepath) { "spec/fixtures/sample_demographics.csv" }
let(:race_codes) do
{ 'American Indian or Alaskan Native' => 1, 'Asian or Pacific Islander' => 2, 'Black or African American' => 3,
'Hispanic or Latinx' => 4, 'White or Caucasian' => 5, 'Race/Ethnicity Not Listed' => 99, 'Middle Eastern' => 8, 'Multiracial' => 100 }
{ "American Indian or Alaskan Native" => 1, "Asian or Pacific Islander" => 2, "Black or African American" => 3,
"Hispanic or Latinx" => 4, "White or Caucasian" => 5, "Race/Ethnicity Not Listed" => 99, "Middle Eastern" => 8, "Multiracial" => 100 }
end
let(:gender_codes) do
{
'Female' => 1, 'Male' => 2, 'Non-Binary' => 4, 'Unknown' => 99
"Female" => 1, "Male" => 2, "Non-Binary" => 4, "Unknown" => 99
}
end
let(:incomes) do
['Economically Disadvantaged N', 'Economically Disadvantaged Y', 'Unknown']
["Economically Disadvantaged N", "Economically Disadvantaged Y", "Unknown"]
end
let(:ells) do
["ELL", "Not ELL", "Unknown"]
end
before :each do
@ -25,12 +29,12 @@ describe DemographicLoader do
DatabaseCleaner.clean
end
describe 'self.load_data' do
it 'does not load qualtrics categories for `prefer not to disclose` or `prefer to self-describe`' do
describe "self.load_data" do
it "does not load qualtrics categories for `prefer not to disclose` or `prefer to self-describe`" do
expect(Race.find_by_qualtrics_code(6)).to be nil
end
it 'loads all racial designations' do
it "loads all racial designations" do
expect(Race.all.count).to eq 8
race_codes.each do |key, value|
expect(Race.find_by_qualtrics_code(value)).not_to eq nil
@ -40,7 +44,7 @@ describe DemographicLoader do
end
end
it 'loads all gender designations' do
it "loads all gender designations" do
expect(Gender.all.count).to eq 4
gender_codes.each do |key, value|
@ -51,11 +55,18 @@ describe DemographicLoader do
end
end
it 'loads all the income designations' do
it "loads all the income designations" do
expect(Income.all.count).to eq 3
incomes.each do |income|
expect(Income.find_by_designation(income).designation).to eq income
end
end
it "loads all the ells designations" do
expect(Ell.all.count).to eq 3
ells.each do |ell|
expect(Ell.find_by_designation(ell).designation).to eq ell
end
end
end
end

@ -0,0 +1,39 @@
require "rails_helper"
require "fileutils"
RSpec.describe DisaggregationLoader do
let(:path) do
Rails.root.join("spec", "fixtures", "disaggregation")
end
let(:academic_year) { create(:academic_year, range: "2022-23") }
let(:district) { create(:district, name: "Maynard Public Schools") }
context ".load" do
it "loads data from the file into a hash" do
data = DisaggregationLoader.new(path:).load
expect(data.values.first.lasid).to eq("1")
expect(data.values.first.academic_year).to eq("2022-23")
expect(data.values.first.district).to eq("Maynard Public Schools")
expect(data.values.last.lasid).to eq("500")
expect(data.values.last.academic_year).to eq("2022-23")
expect(data.values.last.district).to eq("Maynard Public Schools")
end
it "loads income data" do
data = DisaggregationLoader.new(path:).load
expect(data.values.first.raw_income).to eq("Free Lunch")
expect(data.values.last.raw_income).to eq("Not Eligible")
expect(data[["1", "Maynard Public Schools", "2022-23"]].raw_income).to eq("Free Lunch")
expect(data[["2", "Maynard Public Schools", "2022-23"]].raw_income).to eq("Not Eligible")
expect(data[["3", "Maynard Public Schools", "2022-23"]].raw_income).to eq("Reduced Lunch")
end
end
context "Creating a new loader" do
it "creates a directory for the loader file" do
DisaggregationLoader.new(path:)
expect(path).to exist
end
end
end

@ -0,0 +1,108 @@
require "rails_helper"
RSpec.describe DisaggregationRow do
let(:headers) do
["District", "Academic Year", "LASID", "HispanicLatino", "Race", "Gender", "SpecialEdStatus", "In 504 Plan",
"LowIncome", "EL Student First Year"]
end
context ".district" do
context "when the column heading is any upper or lowercase variant of the word district" do
it "returns the correct value for district" do
row = { "District" => "Maynard Public Schools" }
expect(DisaggregationRow.new(row:, headers:).district).to eq "Maynard Public Schools"
headers = ["dISTRICT"]
headers in [district]
row = { district => "Maynard Public Schools" }
expect(DisaggregationRow.new(row:, headers:).district).to eq "Maynard Public Schools"
end
end
end
context ".academic_year" do
context "when the column heading is any upper or lowercase variant of the words academic year" do
it "returns the correct value for district" do
row = { "Academic Year" => "2022-23" }
expect(DisaggregationRow.new(row:, headers:).academic_year).to eq "2022-23"
headers = ["aCADEMIC yEAR"]
headers in [academic_year]
row = { academic_year => "2022-23" }
expect(DisaggregationRow.new(row:, headers:).academic_year).to eq "2022-23"
headers = ["AcademicYear"]
headers in [academic_year]
row = { academic_year => "2022-23" }
expect(DisaggregationRow.new(row:, headers:).academic_year).to eq "2022-23"
end
end
end
context ".raw_income" do
context "when the column heading is any upper or lowercase variant of the words low income" do
it "returns the correct value for low_income" do
row = { "LowIncome" => "Free Lunch" }
expect(DisaggregationRow.new(row:, headers:).raw_income).to eq "Free Lunch"
headers = ["Low income"]
headers in [income]
row = { income => "Free Lunch" }
expect(DisaggregationRow.new(row:, headers:).raw_income).to eq "Free Lunch"
headers = ["LoW InCOme"]
headers in [income]
row = { income => "Free Lunch" }
expect(DisaggregationRow.new(row:, headers:).raw_income).to eq "Free Lunch"
end
end
end
context ".lasid" do
context "when the column heading is any upper or lowercase variant of the words lasid" do
it "returns the correct value for lasid" do
row = { "LASID" => "2366" }
expect(DisaggregationRow.new(row:, headers:).lasid).to eq "2366"
headers = ["LaSiD"]
headers in [lasid]
row = { lasid => "2366" }
expect(DisaggregationRow.new(row:, headers:).lasid).to eq "2366"
end
end
end
context ".ell" do
context "when the column heading is any upper or lowercase variant of the words 'ELL' or 'El Student First Year'" do
it "returns the correct value for a student" do
row = { "EL Student First Year" => "LEP student 1st year" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "ELL"
headers = ["EL Student First Year"]
headers in [ell]
row = { ell => "LEP student not 1st year" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "ELL"
headers = ["EL Student First Year"]
headers in [ell]
row = { ell => "Does not apply" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "Not ELL"
headers = ["EL Student First Year"]
headers in [ell]
row = { ell => "Unknown" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "Unknown"
headers = ["EL Student First Year"]
headers in [ell]
row = { ell => "Any other text" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "Unknown"
headers = ["EL Student First Year"]
headers in [ell]
row = { ell => "" }
expect(DisaggregationRow.new(row:, headers:).ell).to eq "Unknown"
end
end
end
end

@ -88,7 +88,7 @@ describe StudentLoader do
describe "self.load_data" do
context "load student data for all schools" do
before :each do
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses
StudentLoader.load_data filepath: path_to_student_responses
end
@ -102,7 +102,7 @@ describe StudentLoader do
# TODO: get this test to run correctly. Since we are no longer seeding, we need to define schools, and districts; some Lowell, some not
xcontext "When using the rule to skip non Lowell schools" do
before :each do
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses
StudentLoader.load_data filepath: path_to_student_responses, rules: [Rule::SkipNonLowellSchools]
end

@ -7,12 +7,7 @@ RSpec.describe SurveyItemValues, type: :model do
end
let(:genders) do
create(:gender, qualtrics_code: 1)
gender_hash = {}
Gender.all.each do |gender|
gender_hash[gender.qualtrics_code] = gender
end
gender_hash
Gender.by_qualtrics_code
end
let(:survey_items) { [] }
let(:district) { create(:district, name: "Attleboro") }
@ -169,79 +164,90 @@ RSpec.describe SurveyItemValues, type: :model do
end
context ".income" do
context "when no disaggregation data is provided" do
it "returns an empty string " do
disaggregation_data = {}
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:, disaggregation_data:)
expect(values.income).to eq "Unknown"
end
before :each do
attleboro
ay_2022_23
end
context "when disaggregation data is provided" do
before :each do
attleboro
ay_2022_23
end
it "translates Free Lunch to Economically Disadvantaged - Y" do
headers = ["LowIncome"]
row = { "LowIncome" => "Free Lunch" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
it "translates Free Lunch to Economically Disadvantaged - Y" do
headers = ["District", "Academic Year", "LASID", "LowIncome"]
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Free Lunch" }
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
it "translates Reduced Lunch to Economically Disadvantaged - Y" do
headers = ["LowIncome"]
row = { "LowIncome" => "Reduced Lunch" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
headers = ["LASID", "Dese Id", "RecordedDate"]
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
disaggregation_data:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
it "translates LowIncome to Economically Disadvantaged - Y" do
headers = ["LowIncome"]
row = { "LowIncome" => "LowIncome" }
it "translates Reduced Lunch to Economically Disadvantaged - Y" do
headers = ["District", "Academic Year", "LASID", "LowIncome"]
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Reduced Lunch" }
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
headers = ["LASID", "Dese Id", "RecordedDate"]
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
disaggregation_data:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
it "translates Not Eligible to Economically Disadvantaged - N" do
headers = ["LowIncome"]
row = { "LowIncome" => "Not Eligible" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.income).to eq "Economically Disadvantaged - N"
end
it "translates LowIncome to Economically Disadvantaged - Y" do
headers = ["District", "Academic Year", "LASID", "LowIncome"]
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "LowIncome" }
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
it "translates blanks to Unknown" do
headers = ["LowIncome"]
row = { "LowIncome" => "" }
headers = ["LASID", "Dese Id", "RecordedDate"]
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
disaggregation_data:)
expect(values.income).to eq "Economically Disadvantaged - Y"
end
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.income).to eq "Unknown"
end
end
it "translates Not Eligible to Economically Disadvantaged - N" do
headers = ["District", "Academic Year", "LASID", "LowIncome"]
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Not Eligible" }
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
context ".ell" do
before :each do
attleboro
ay_2022_23
end
headers = ["LASID", "Dese Id", "RecordedDate"]
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
disaggregation_data:)
expect(values.income).to eq "Economically Disadvantaged - N"
end
it 'translates "LEP Student 1st Year" or "LEP Student Not 1st Year" into ELL' do
headers = ["Raw ELL"]
row = { "Raw ELL" => "LEP Student 1st Year" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "ELL"
it "translates blanks to Unknown" do
headers = ["District", "Academic Year", "LASID", "LowIncome"]
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "" }
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
row = { "Raw ELL" => "LEP Student Not 1st Year" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "ELL"
headers = ["LASID", "Dese Id", "RecordedDate"]
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
disaggregation_data:)
expect(values.income).to eq "Unknown"
end
row = { "Raw ELL" => "LEP Student Not 1st Year" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "ELL"
end
it 'translates "Does not Apply" into "Not ELL"' do
headers = ["Raw ELL"]
row = { "Raw ELL" => "Does not apply" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "Not ELL"
row = { "Raw ELL" => "Does Not APPLY" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "Not ELL"
end
it 'tranlsates blanks into "Unknown"' do
headers = ["Raw ELL"]
row = { "Raw ELL" => "" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "Unknown"
row = { "Raw ELL" => "Anything else" }
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
expect(values.ell).to eq "Unknown"
end
end

@ -1,59 +1,62 @@
require 'rails_helper'
require "rails_helper"
describe SurveyResponsesDataLoader do
let(:path_to_teacher_responses) { Rails.root.join('spec', 'fixtures', 'test_2020-21_teacher_survey_responses.csv') }
let(:path_to_student_responses) { Rails.root.join('spec', 'fixtures', 'test_2020-21_student_survey_responses.csv') }
let(:path_to_teacher_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_teacher_survey_responses.csv") }
let(:path_to_student_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_student_survey_responses.csv") }
let(:path_to_butler_student_responses) do
Rails.root.join('spec', 'fixtures', 'test_2022-23_butler_student_survey_responses.csv')
Rails.root.join("spec", "fixtures", "test_2022-23_butler_student_survey_responses.csv")
end
let(:ay_2020_21) { create(:academic_year, range: '2020-21') }
let(:ay_2022_23) { create(:academic_year, range: '2022-23') }
let(:ay_2020_21) { create(:academic_year, range: "2020-21") }
let(:ay_2022_23) { create(:academic_year, range: "2022-23") }
let(:school) { create(:school, name: 'Lee Elementary School', slug: 'lee-elementary-school', dese_id: 1_500_025) }
let(:lowell) { create(:district, name: 'Lowell', slug: 'lowell') }
let(:school) { create(:school, name: "Lee Elementary School", slug: "lee-elementary-school", dese_id: 1_500_025) }
let(:lowell) { create(:district, name: "Lowell", slug: "lowell") }
let(:second_school) do
create(:school, name: 'Lee Middle High School', slug: 'lee-middle-high-school', dese_id: 1_500_505,
create(:school, name: "Lee Middle High School", slug: "lee-middle-high-school", dese_id: 1_500_505,
district: lowell)
end
let(:butler_school) do
create(:school, name: 'Butler Elementary School', slug: 'butler-elementary-school', dese_id: 1_600_310,
create(:school, name: "Butler Elementary School", slug: "butler-elementary-school", dese_id: 1_600_310,
district: lowell)
end
let(:t_pcom_q3) { create(:survey_item, survey_item_id: 't-pcom-q3') }
let(:t_pcom_q2) { create(:survey_item, survey_item_id: 't-pcom-q2') }
let(:t_coll_q1) { create(:survey_item, survey_item_id: 't-coll-q1') }
let(:t_coll_q2) { create(:survey_item, survey_item_id: 't-coll-q2') }
let(:t_coll_q3) { create(:survey_item, survey_item_id: 't-coll-q3') }
let(:t_sach_q1) { create(:survey_item, survey_item_id: 't-sach-q1') }
let(:t_sach_q2) { create(:survey_item, survey_item_id: 't-sach-q2') }
let(:t_sach_q3) { create(:survey_item, survey_item_id: 't-sach-q3') }
let(:s_phys_q1) { create(:survey_item, survey_item_id: 's-phys-q1') }
let(:s_phys_q2) { create(:survey_item, survey_item_id: 's-phys-q2') }
let(:s_phys_q3) { create(:survey_item, survey_item_id: 's-phys-q3') }
let(:s_phys_q4) { create(:survey_item, survey_item_id: 's-phys-q4') }
let(:s_vale_q1) { create(:survey_item, survey_item_id: 's-phys-q1') }
let(:s_vale_q2) { create(:survey_item, survey_item_id: 's-phys-q2') }
let(:s_vale_q3) { create(:survey_item, survey_item_id: 's-phys-q3') }
let(:s_vale_q4) { create(:survey_item, survey_item_id: 's-phys-q4') }
let(:s_acst_q1) { create(:survey_item, survey_item_id: 's-acst-q1') }
let(:s_acst_q2) { create(:survey_item, survey_item_id: 's-acst-q2') }
let(:s_acst_q3) { create(:survey_item, survey_item_id: 's-acst-q3') }
let(:s_acst_q4) { create(:survey_item, survey_item_id: 's-acst-q4') }
let(:s_emsa_q1) { create(:survey_item, survey_item_id: 's-emsa-q1') }
let(:s_emsa_q2) { create(:survey_item, survey_item_id: 's-emsa-q2') }
let(:s_emsa_q3) { create(:survey_item, survey_item_id: 's-emsa-q3') }
let(:t_pcom_q3) { create(:survey_item, survey_item_id: "t-pcom-q3") }
let(:t_pcom_q2) { create(:survey_item, survey_item_id: "t-pcom-q2") }
let(:t_coll_q1) { create(:survey_item, survey_item_id: "t-coll-q1") }
let(:t_coll_q2) { create(:survey_item, survey_item_id: "t-coll-q2") }
let(:t_coll_q3) { create(:survey_item, survey_item_id: "t-coll-q3") }
let(:t_sach_q1) { create(:survey_item, survey_item_id: "t-sach-q1") }
let(:t_sach_q2) { create(:survey_item, survey_item_id: "t-sach-q2") }
let(:t_sach_q3) { create(:survey_item, survey_item_id: "t-sach-q3") }
let(:s_phys_q1) { create(:survey_item, survey_item_id: "s-phys-q1") }
let(:s_phys_q2) { create(:survey_item, survey_item_id: "s-phys-q2") }
let(:s_phys_q3) { create(:survey_item, survey_item_id: "s-phys-q3") }
let(:s_phys_q4) { create(:survey_item, survey_item_id: "s-phys-q4") }
let(:s_vale_q1) { create(:survey_item, survey_item_id: "s-phys-q1") }
let(:s_vale_q2) { create(:survey_item, survey_item_id: "s-phys-q2") }
let(:s_vale_q3) { create(:survey_item, survey_item_id: "s-phys-q3") }
let(:s_vale_q4) { create(:survey_item, survey_item_id: "s-phys-q4") }
let(:s_acst_q1) { create(:survey_item, survey_item_id: "s-acst-q1") }
let(:s_acst_q2) { create(:survey_item, survey_item_id: "s-acst-q2") }
let(:s_acst_q3) { create(:survey_item, survey_item_id: "s-acst-q3") }
let(:s_acst_q4) { create(:survey_item, survey_item_id: "s-acst-q4") }
let(:s_emsa_q1) { create(:survey_item, survey_item_id: "s-emsa-q1") }
let(:s_emsa_q2) { create(:survey_item, survey_item_id: "s-emsa-q2") }
let(:s_emsa_q3) { create(:survey_item, survey_item_id: "s-emsa-q3") }
let(:female) { create(:gender, qualtrics_code: 1) }
let(:male) { create(:gender, qualtrics_code: 2) }
let(:another_gender) { create(:gender, qualtrics_code: 3) }
let(:non_binary) { create(:gender, qualtrics_code: 4) }
let(:unknown_gender) { create(:gender, qualtrics_code: 99) }
let(:low_income) { create(:income, designation: 'Economically Disadvantaged Y') }
let(:high_income) { create(:income, designation: 'Economically Disadvantaged N') }
let(:unknown_income) { create(:income, designation: 'Unknown') }
let(:low_income) { create(:income, designation: "Economically Disadvantaged Y") }
let(:high_income) { create(:income, designation: "Economically Disadvantaged N") }
let(:unknown_income) { create(:income, designation: "Unknown") }
let(:yes_ell) { create(:ell, designation: "ELL") }
let(:not_ell) { create(:ell, designation: "Not ELL") }
let(:unknown_ell) { create(:ell, designation: "Unknown") }
let(:setup) do
ay_2020_21
@ -92,18 +95,21 @@ describe SurveyResponsesDataLoader do
low_income
high_income
unknown_income
yes_ell
not_ell
unknown_ell
end
before :each do
setup
end
describe 'loading teacher survey responses' do
describe "loading teacher survey responses" do
before do
SurveyResponsesDataLoader.load_data filepath: path_to_teacher_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_teacher_responses
end
it 'ensures teacher responses load correctly' do
it "ensures teacher responses load correctly" do
assigns_academic_year_to_survey_item_responses
assigns_school_to_the_survey_item_responses
assigns_recorded_date_to_teacher_responses
@ -114,12 +120,12 @@ describe SurveyResponsesDataLoader do
end
end
describe 'student survey responses' do
describe "student survey responses" do
before do
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses
end
it 'ensures student responses load correctly' do
it "ensures student responses load correctly" do
assigns_academic_year_to_student_survey_item_responses
assigns_school_to_student_survey_item_responses
assigns_recorded_date_to_student_responses
@ -129,85 +135,86 @@ describe SurveyResponsesDataLoader do
assigns_grade_level_to_responses
assigns_gender_to_responses
assigns_income_to_responses
assigns_ell_to_responses
is_idempotent_for_students
end
context 'when updating student survey responses from another csv file' do
context "when updating student survey responses from another csv file" do
before :each do
SurveyResponsesDataLoader.load_data filepath: Rails.root.join('spec', 'fixtures',
'secondary_test_2020-21_student_survey_responses.csv')
SurveyResponsesDataLoader.new.load_data filepath: Rails.root.join("spec", "fixtures",
"secondary_test_2020-21_student_survey_responses.csv")
end
it 'updates the likert score to the score on the new csv file' do
s_emsa_q1 = SurveyItem.find_by_survey_item_id 's-emsa-q1'
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3',
it "updates the likert score to the score on the new csv file" do
s_emsa_q1 = SurveyItem.find_by_survey_item_id "s-emsa-q1"
expect(SurveyItemResponse.where(response_id: "student_survey_response_3",
survey_item: s_emsa_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4',
expect(SurveyItemResponse.where(response_id: "student_survey_response_4",
survey_item: s_emsa_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5',
expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
survey_item: s_emsa_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5',
expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
survey_item: s_acst_q3).first.likert_score).to eq 4
end
end
end
# figure out why this is failing
describe 'when using Lowell rules to skip rows in the csv file' do
describe "when using Lowell rules to skip rows in the csv file" do
before :each do
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses,
rules: [Rule::SkipNonLowellSchools]
SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses,
rules: [Rule::SkipNonLowellSchools]
end
it 'rejects any non-lowell school' do
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').count).to eq 0
it "rejects any non-lowell school" do
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").count).to eq 0
expect(SurveyItemResponse.count).to eq 69
end
it 'loads the correct number of responses for lowell schools' do
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').count).to eq 0
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').count).to eq 12
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').count).to eq 15
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').count).to eq 14
it "loads the correct number of responses for lowell schools" do
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").count).to eq 0
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").count).to eq 12
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").count).to eq 15
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").count).to eq 14
end
context 'when loading 22-23 butler survey responses' do
context "when loading 22-23 butler survey responses" do
before :each do
SurveyResponsesDataLoader.load_data filepath: path_to_butler_student_responses,
rules: [Rule::SkipNonLowellSchools]
SurveyResponsesDataLoader.new.load_data filepath: path_to_butler_student_responses,
rules: [Rule::SkipNonLowellSchools]
end
it 'loads all the responses for Butler' do
it "loads all the responses for Butler" do
expect(SurveyItemResponse.where(school: butler_school).count).to eq 56
end
it 'blank entries for grade get loaded as nils, not zero values' do
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_1').first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_2').first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_3').first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_4').first.grade).to eq 5
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_5').first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_6').first.grade).to eq 6
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_7').first.grade).to eq nil
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_8').first.grade).to eq 0
it "blank entries for grade get loaded as nils, not zero values" do
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_1").first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_2").first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_3").first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_4").first.grade).to eq 5
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_5").first.grade).to eq 7
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_6").first.grade).to eq 6
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_7").first.grade).to eq nil
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_8").first.grade).to eq 0
end
end
end
end
def assigns_academic_year_to_survey_item_responses
expect(SurveyItemResponse.find_by_response_id('teacher_survey_response_1').academic_year).to eq ay_2020_21
expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").academic_year).to eq ay_2020_21
end
def assigns_school_to_the_survey_item_responses
expect(SurveyItemResponse.find_by_response_id('teacher_survey_response_1').school).to eq school
expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").school).to eq school
end
def loads_survey_item_responses_for_a_given_survey_response
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').count).to eq 5
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').count).to eq 0
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').count).to eq 8
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').count).to eq 8
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').count).to eq 8
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").count).to eq 5
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").count).to eq 0
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").count).to eq 8
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").count).to eq 8
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").count).to eq 8
end
def loads_all_survey_item_responses_for_a_given_survey_item
@ -216,44 +223,44 @@ def loads_all_survey_item_responses_for_a_given_survey_item
end
def captures_likert_scores_for_survey_item_responses
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').where(survey_item: t_pcom_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').where(survey_item: t_pcom_q3).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q3).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').where(survey_item: t_pcom_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').where(survey_item: t_pcom_q3)).to be_empty
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q3)).to be_empty
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').where(survey_item: t_pcom_q2).first.likert_score).to eq 5
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').where(survey_item: t_pcom_q3).first.likert_score).to eq 5
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q2).first.likert_score).to eq 5
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q3).first.likert_score).to eq 5
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').where(survey_item: t_pcom_q2).first.likert_score).to eq 4
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').where(survey_item: t_pcom_q3).first.likert_score).to eq 4
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q2).first.likert_score).to eq 4
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q3).first.likert_score).to eq 4
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').where(survey_item: t_pcom_q2).first.likert_score).to eq 2
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').where(survey_item: t_pcom_q3).first.likert_score).to eq 4
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q2).first.likert_score).to eq 2
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q3).first.likert_score).to eq 4
end
def is_idempotent
number_of_survey_item_responses = SurveyItemResponse.count
SurveyResponsesDataLoader.load_data filepath: path_to_teacher_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_teacher_responses
expect(SurveyItemResponse.count).to eq number_of_survey_item_responses
end
def assigns_academic_year_to_student_survey_item_responses
expect(SurveyItemResponse.find_by_response_id('student_survey_response_3').academic_year).to eq ay_2020_21
expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").academic_year).to eq ay_2020_21
end
def assigns_school_to_student_survey_item_responses
expect(SurveyItemResponse.find_by_response_id('student_survey_response_3').school).to eq second_school
expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").school).to eq second_school
end
def loads_student_survey_item_response_values
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').count).to eq 3
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').count).to eq 0
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').count).to eq 12
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').count).to eq 15
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').count).to eq 14
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").count).to eq 3
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").count).to eq 0
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").count).to eq 12
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").count).to eq 15
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").count).to eq 14
end
def student_survey_item_response_count_matches_expected
@ -262,37 +269,37 @@ def student_survey_item_response_count_matches_expected
end
def captures_likert_scores_for_student_survey_item_responses
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').where(survey_item: s_phys_q1).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').where(survey_item: s_phys_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q1).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').where(survey_item: s_phys_q1)).to be_empty
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').where(survey_item: s_phys_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q1)).to be_empty
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q2)).to be_empty
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').where(survey_item: s_phys_q2).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q2).first.likert_score).to eq 3
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').where(survey_item: s_phys_q2).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q2).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').where(survey_item: s_phys_q2).first.likert_score).to eq 2
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q1).first.likert_score).to eq 1
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q2).first.likert_score).to eq 2
end
def is_idempotent_for_students
number_of_survey_item_responses = SurveyItemResponse.count
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses
expect(SurveyItemResponse.count).to eq number_of_survey_item_responses
end
def assigns_grade_level_to_responses
results = { 'student_survey_response_1' => 11,
'student_survey_response_3' => 8,
'student_survey_response_4' => 8,
'student_survey_response_5' => 7,
'student_survey_response_6' => 3,
'student_survey_response_7' => 4 }
results = { "student_survey_response_1" => 11,
"student_survey_response_3" => 8,
"student_survey_response_4" => 8,
"student_survey_response_5" => 7,
"student_survey_response_6" => 3,
"student_survey_response_7" => 4 }
results.each do |key, value|
expect(SurveyItemResponse.where(response_id: key).all? do |response|
response.grade == value
@ -301,12 +308,12 @@ def assigns_grade_level_to_responses
end
def assigns_gender_to_responses
results = { 'student_survey_response_1' => female,
'student_survey_response_3' => male,
'student_survey_response_4' => non_binary,
'student_survey_response_5' => non_binary,
'student_survey_response_6' => unknown_gender,
'student_survey_response_7' => unknown_gender }
results = { "student_survey_response_1" => female,
"student_survey_response_3" => male,
"student_survey_response_4" => non_binary,
"student_survey_response_5" => non_binary,
"student_survey_response_6" => unknown_gender,
"student_survey_response_7" => unknown_gender }
results.each do |key, value|
expect(SurveyItemResponse.where(response_id: key).first.gender).to eq value
@ -314,36 +321,51 @@ def assigns_gender_to_responses
end
def assigns_recorded_date_to_student_responses
results = { 'student_survey_response_1' => '2020-09-30T18:48:50',
'student_survey_response_3' => '2021-03-31T09:59:02',
'student_survey_response_4' => '2021-03-31T10:00:17',
'student_survey_response_5' => '2021-03-31T10:01:36',
'student_survey_response_6' => '2021-03-31T10:01:37',
'student_survey_response_7' => '2021-03-31T10:01:38' }
results = { "student_survey_response_1" => "2020-09-30T18:48:50",
"student_survey_response_3" => "2021-03-31T09:59:02",
"student_survey_response_4" => "2021-03-31T10:00:17",
"student_survey_response_5" => "2021-03-31T10:01:36",
"student_survey_response_6" => "2021-03-31T10:01:37",
"student_survey_response_7" => "2021-03-31T10:01:38" }
results.each do |key, value|
expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
end
end
def assigns_recorded_date_to_teacher_responses
results = { 'teacher_survey_response_1' => '2020-10-16 11:09:03',
'teacher_survey_response_3' => '2020-12-06 8:36:52',
'teacher_survey_response_4' => '2020-12-06 8:51:25',
'teacher_survey_response_5' => '2020-12-06 8:55:58' }
results = { "teacher_survey_response_1" => "2020-10-16 11:09:03",
"teacher_survey_response_3" => "2020-12-06 8:36:52",
"teacher_survey_response_4" => "2020-12-06 8:51:25",
"teacher_survey_response_5" => "2020-12-06 8:55:58" }
results.each do |key, value|
expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
end
end
def assigns_income_to_responses
results = { 'student_survey_response_1' => low_income,
'student_survey_response_3' => low_income,
'student_survey_response_4' => unknown_income,
'student_survey_response_5' => low_income,
'student_survey_response_6' => high_income,
'student_survey_response_7' => low_income }
results = { "student_survey_response_1" => low_income,
"student_survey_response_3" => low_income,
"student_survey_response_4" => unknown_income,
"student_survey_response_5" => low_income,
"student_survey_response_6" => high_income,
"student_survey_response_7" => low_income }
results.each do |key, value|
expect(SurveyItemResponse.where(response_id: key).first.income).to eq value
income = SurveyItemResponse.find_by_response_id(key).income
expect(income).to eq value
end
end
def assigns_ell_to_responses
results = { "student_survey_response_1" => not_ell,
"student_survey_response_3" => unknown_ell,
"student_survey_response_4" => yes_ell,
"student_survey_response_5" => yes_ell,
"student_survey_response_6" => unknown_ell,
"student_survey_response_7" => unknown_ell }
results.each do |key, value|
ell = SurveyItemResponse.find_by_response_id(key).ell
expect(ell).to eq value
end
end

Loading…
Cancel
Save