mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
import students
This commit is contained in:
parent
8c7767d0b9
commit
12e4e3f177
28 changed files with 11093 additions and 10843 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
plugins:
|
plugins:
|
||||||
- solargraph-reek
|
- solargraph-reek
|
||||||
reporters:
|
reporters:
|
||||||
- reek
|
-reek
|
||||||
|
|
|
||||||
3
Gemfile
3
Gemfile
|
|
@ -60,6 +60,7 @@ group :development, :test do
|
||||||
gem 'rack-mini-profiler'
|
gem 'rack-mini-profiler'
|
||||||
gem 'rspec-rails', '~> 5.1.0'
|
gem 'rspec-rails', '~> 5.1.0'
|
||||||
gem 'standard'
|
gem 'standard'
|
||||||
|
gem 'debug', platforms: %i[mri mingw x64_mingw]
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
|
@ -70,7 +71,7 @@ group :development do
|
||||||
gem 'erblint-github'
|
gem 'erblint-github'
|
||||||
gem 'listen', '~> 3.0.5'
|
gem 'listen', '~> 3.0.5'
|
||||||
gem 'nested_scaffold'
|
gem 'nested_scaffold'
|
||||||
gem 'reek', require: false
|
# gem 'reek', require: false
|
||||||
gem 'rubocop', require: false
|
gem 'rubocop', require: false
|
||||||
gem 'seed_dump'
|
gem 'seed_dump'
|
||||||
gem 'solargraph-reek'
|
gem 'solargraph-reek'
|
||||||
|
|
|
||||||
12
Gemfile.lock
12
Gemfile.lock
|
|
@ -119,6 +119,9 @@ GEM
|
||||||
activerecord (>= 5.a)
|
activerecord (>= 5.a)
|
||||||
database_cleaner-core (~> 2.0.0)
|
database_cleaner-core (~> 2.0.0)
|
||||||
database_cleaner-core (2.0.1)
|
database_cleaner-core (2.0.1)
|
||||||
|
debug (1.6.1)
|
||||||
|
irb (>= 1.3.6)
|
||||||
|
reline (>= 0.3.1)
|
||||||
devise (4.8.1)
|
devise (4.8.1)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
|
|
@ -157,6 +160,9 @@ GEM
|
||||||
html_tokenizer (0.0.7)
|
html_tokenizer (0.0.7)
|
||||||
i18n (1.12.0)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
|
io-console (0.5.11)
|
||||||
|
irb (1.4.1)
|
||||||
|
reline (>= 0.3.0)
|
||||||
jaro_winkler (1.5.4)
|
jaro_winkler (1.5.4)
|
||||||
jbuilder (2.11.5)
|
jbuilder (2.11.5)
|
||||||
actionview (>= 5.0.0)
|
actionview (>= 5.0.0)
|
||||||
|
|
@ -207,8 +213,6 @@ GEM
|
||||||
timeout
|
timeout
|
||||||
newrelic_rpm (8.8.0)
|
newrelic_rpm (8.8.0)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.13.7-x86_64-darwin)
|
|
||||||
racc (~> 1.4)
|
|
||||||
nokogiri (1.13.7-x86_64-linux)
|
nokogiri (1.13.7-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
omniauth (2.1.0)
|
omniauth (2.1.0)
|
||||||
|
|
@ -274,6 +278,8 @@ GEM
|
||||||
parser (~> 3.1.0)
|
parser (~> 3.1.0)
|
||||||
rainbow (>= 2.0, < 4.0)
|
rainbow (>= 2.0, < 4.0)
|
||||||
regexp_parser (2.5.0)
|
regexp_parser (2.5.0)
|
||||||
|
reline (0.3.1)
|
||||||
|
io-console (~> 0.5)
|
||||||
responders (3.0.1)
|
responders (3.0.1)
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
|
|
@ -404,6 +410,7 @@ DEPENDENCIES
|
||||||
capybara
|
capybara
|
||||||
cssbundling-rails
|
cssbundling-rails
|
||||||
database_cleaner
|
database_cleaner
|
||||||
|
debug
|
||||||
devise
|
devise
|
||||||
erb_lint
|
erb_lint
|
||||||
erblint-github
|
erblint-github
|
||||||
|
|
@ -426,7 +433,6 @@ DEPENDENCIES
|
||||||
rails (~> 7.0.3)
|
rails (~> 7.0.3)
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
redis (~> 3.0)
|
redis (~> 3.0)
|
||||||
reek
|
|
||||||
rspec-rails (~> 5.1.0)
|
rspec-rails (~> 5.1.0)
|
||||||
rubocop
|
rubocop
|
||||||
seed_dump
|
seed_dump
|
||||||
|
|
|
||||||
|
|
@ -183,10 +183,11 @@ class Measure < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def sufficient_student_data?(school:, academic_year:)
|
def sufficient_student_data?(school:, academic_year:)
|
||||||
return @sufficient_student_data ||= false unless includes_student_survey_items?
|
false unless includes_student_survey_items?
|
||||||
return @sufficient_student_data ||= false if no_student_responses_exist?(school:, academic_year:)
|
false if no_student_responses_exist?(school:, academic_year:)
|
||||||
|
|
||||||
@sufficient_student_data ||= subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
# this gets memoized on first run so check to make sure
|
||||||
|
subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||||
end
|
end
|
||||||
|
|
||||||
def sufficient_teacher_data?(school:, academic_year:)
|
def sufficient_teacher_data?(school:, academic_year:)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
class Race < ApplicationRecord
|
class Race < ApplicationRecord
|
||||||
include FriendlyId
|
include FriendlyId
|
||||||
|
has_many :student_races
|
||||||
|
has_many :students, through: :student_races
|
||||||
friendly_id :designation, use: [:slugged]
|
friendly_id :designation, use: [:slugged]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
7
app/models/student.rb
Normal file
7
app/models/student.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
class Student < ApplicationRecord
|
||||||
|
has_many :survey_item_responses
|
||||||
|
has_many :student_races
|
||||||
|
has_many :races, through: :student_races
|
||||||
|
|
||||||
|
encrypts :lasid, deterministic: true
|
||||||
|
end
|
||||||
4
app/models/student_race.rb
Normal file
4
app/models/student_race.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
class StudentRace < ApplicationRecord
|
||||||
|
belongs_to :student
|
||||||
|
belongs_to :race
|
||||||
|
end
|
||||||
|
|
@ -7,6 +7,8 @@ class SurveyItemResponse < ActiveRecord::Base
|
||||||
belongs_to :academic_year
|
belongs_to :academic_year
|
||||||
belongs_to :school
|
belongs_to :school
|
||||||
belongs_to :survey_item, counter_cache: true
|
belongs_to :survey_item, counter_cache: true
|
||||||
|
belongs_to :student, foreign_key: :student_id, optional: true
|
||||||
|
|
||||||
has_one :measure, through: :survey_item
|
has_one :measure, through: :survey_item
|
||||||
|
|
||||||
scope :exclude_boston, lambda {
|
scope :exclude_boston, lambda {
|
||||||
|
|
|
||||||
74
app/services/student_loader.rb
Normal file
74
app/services/student_loader.rb
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# SurveyItemResponse.where(student: StudentRace.where(race: Race.find_by_qualtrics_code(8)).limit(10).map(&:student)).count
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
|
class StudentLoader
|
||||||
|
def self.load_data(filepath:)
|
||||||
|
File.open(filepath) do |file|
|
||||||
|
headers = file.first
|
||||||
|
|
||||||
|
students = []
|
||||||
|
file.lazy.each_slice(1000) do |lines|
|
||||||
|
CSV.parse(lines.join, headers:).map do |row|
|
||||||
|
# students << process_row(row:)
|
||||||
|
process_row(row:)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Student.import students.compact.flatten.to_set.to_a, batch_size: 1000,
|
||||||
|
# on_duplicate_key_update: { conflict_target: [:id] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.process_row(row:)
|
||||||
|
race_codes = row['RACE'] || row['What is your race/ethnicity?(Please select all that apply) - Selected Choice'] || '99'
|
||||||
|
race_codes = race_codes.split(',').map(&:to_i) || []
|
||||||
|
races = process_races(codes: race_codes)
|
||||||
|
response_id = row['ResponseId'] || row['Responseid'] || row['ResponseID'] ||
|
||||||
|
row['Response ID'] || row['Response id'] || row['Response Id']
|
||||||
|
lasid = row['LASID'] || row['lasid']
|
||||||
|
return nil if student_exists?(response_id:)
|
||||||
|
|
||||||
|
student = create_student(response_id:, lasid:, races:)
|
||||||
|
|
||||||
|
assign_student_to_responses(response_id:, student:)
|
||||||
|
student
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.student_exists?(response_id:)
|
||||||
|
Student.find_by_response_id(response_id).present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.assign_student_to_responses(response_id:, student:)
|
||||||
|
survey_responses = SurveyItemResponse.where(response_id:)
|
||||||
|
survey_responses.each do |response|
|
||||||
|
response.student = student
|
||||||
|
response.save
|
||||||
|
end
|
||||||
|
|
||||||
|
# SurveyItemResponse.import survey_responses, on_duplicate_key_update: { conflict_target: [:id], columns: [:student] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.create_student(response_id:, lasid:, races:)
|
||||||
|
student = Student.new(response_id:)
|
||||||
|
races.each do |race|
|
||||||
|
student.races << race
|
||||||
|
end
|
||||||
|
student.lasid = lasid
|
||||||
|
student.save
|
||||||
|
student
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.process_races(codes:)
|
||||||
|
codes = codes.map do |code|
|
||||||
|
code = 99 if [6, 7].include?(code)
|
||||||
|
Race.find_by_qualtrics_code(code)
|
||||||
|
end
|
||||||
|
remove_unknown_race_if_other_races_present(races: codes.to_set)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.remove_unknown_race_if_other_races_present(races:)
|
||||||
|
races.delete(Race.find_by_qualtrics_code(99)) if races.length > 1
|
||||||
|
races
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1 +1 @@
|
||||||
BT6iQXFC9KSTrl2ERWJBrgTjSC+hpRj7v3VexKptLx5m2H20A4nUNx8ng2z/Px/47FBVtTUpyBBPf1yubbLZWs6VNsKzgmhUGiWjZ3x0UzQChPN6tgR1dNN34Olbr8eBvlWacb/VwrJysoHf+OR+MpPWdxqZNgoruqHXgJgU7xs24+MUVUiqUC80v0GDzar7aTEwXZFIOA==--O8YzbfYG1w8ZcEVp--He1+cpuOPDgYOAEXRLyzxA==
|
CCr3H/C8F7QRySYJv3rjuU7vTlntvEc84yqA5Ll76J5bwsJpYnbPymlO66Y2TzwAJe2+4Ft8TCspBr0lnfKM8gKdhUWkbyxwUOdiFi/5yilI/k7fE1W2mlPVUQHyePoq0+VolP4TITIcrXvstLCV42ooXeKYrHnRw+Nql3VzKRRJlmtnt9HxLsta+L5p3emCnUTL96LszUjYMtdB8T4uujyHjmKqzC+5lS2IqfUeW82g8LfeuTVcxqiKUce+ZywBGZjLlOIrFVndqV9vAV6U6hOnqXzEW7Tl184zt5OIfq6qkhKp/L2xTnJxVFZS+eNCiffBqQ==--PsX6hrtFTNQaK+zu--N0QfgNulvjEoGj6Nl6iE7g==
|
||||||
|
|
@ -1 +1 @@
|
||||||
KX2iu27u/BXglSTHH4yD2ApSFEgZxn9C3EyJq151WIMH8ZSI6ThlF4tOhekwXkbTBxH4--2INMwwXnnq/Q1ur4--RBKY0uu9FmKTbxarieTWiQ==
|
pEFclvEWB+uo//cOuEU7FHhFBNOMyDFTtc8NZeKmqPPaHI0MzfTonSrYMSD2awmUI5hbm+OJaGmBBElpVmGExEXH8Jc3282Vxrk3szBIWuq0rulg227hcatVtlRVri1vyhUCDPMOz89jc1FnHx/zMW62zRYgwIrNjZ7BU2WaoELy+xz8EKEEIcXC47Zg4wBM/omGuj2fkcO3R5Fvm39H0dAYhPL/JzFsUpyXGqXJFMNVN1ImJlNX8cfVYj31VMM4EROB6XOR1Hnx/Hq6AqAbFH/v5lh+B6KgMJoYA2AEOsZdj+y6NuCIXhxn0HnAlEqxFf9MSw==--QMoBHlvTP2/cnr4q--rpnKseZ3ToVmMDKE9dhqkA==
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
R+Ip8wqfVzRwuVraYG4cubh3oUHbJjat8ddjCwB30HQLKIOsuxiSH9oUEP2yTmXG3iTx--t7wIdTiRrs7eOVB7--0x4CV22/Jl2ElPzaFT/ICA==
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
LVLki98j5ziVOtGhQhXvB/iCF3qVFR98ztFGyi+uFYh1Ma2sRu72TJGMc0Wk29BD15F0ytNsgmBpQUwmk4Mrwoq7RIAd8sODbnfD9ScC4eguvCS8gN8NpU3NYnuZEbrw3xFOvH+apfKYlo5jomoGV2hytEW7WQkUNIE+M1TjYU0FFYkKs0F6ovw/xdMBGhzFZndjfU4BAg==--NE3oTAKz9bImGD1M--vlT/dy8GZunUkoovq2g+SA==
|
9WrwvS2cg9KTeIG7QRV22i13cNQZifGdx3x9SK/FfHTIZgxAeoK2tzDv6kr51+CPBeZxK1jPazx4fKh0/UVkRTF+KoxG376Sw1RnV72ahd47xPdaRUzbWWKcsEErIDc9BsLEThhHA4CG6ZcQmF7h42vvfLDbVrResL6dz5PZfWRtN20o1nMPJnJRHP+9umgkWo3QU7qr/7vYfu9C5iSRmaColnO7vaGR+pglCfY+ezbkAI3vZx3uleepvygnFDcrptqCbrGs/gsvxq/sXJbG2MsEiZk+NWWr7ApcGPjtLsRjjXqNZ7p3vbTiYIviDe9JPbdXoc9NXN/fp4REMX4=--2MXdu/2sPS/aO59/--iy1RPBMtri9m/qor0xgK0A==
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
,nelson,tripper,29.07.2022 14:28,file:///home/nelson/.config/libreoffice/4;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
StartDate,EndDate,Status,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LocationLatitude,LocationLongitude,DistributionChannel,UserLanguage,What district is your school in?,DESE ID,"Please select the month, day and year of your birthday. - Month","Please select the month, day and year of your birthday. - Day","Please select the month, day and year of your birthday. - Year",Please enter your Locally Assigned Student ID Number (LASID).,What grade are you in?,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-cure-q1,s-cure-q2,s-cure-q3,s-cure-q4,s-sten-q1,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,s-grmi-q2,s-grmi-q3,s-grmi-q4,s-appa-q1,s-appa-q2,s-appa-q3,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,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-sust-q1,s-sust-q2,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,s-tint-q2,s-tint-q3,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,Q90,Q90_3_TEXT,Q91,Q91_7_TEXT,Q_Language
|
StartDate,EndDate,Status,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LocationLatitude,LocationLongitude,DistributionChannel,UserLanguage,What district is your school in?,DESE ID,"Please select the month, day and year of your birthday. - Month","Please select the month, day and year of your birthday. - Day","Please select the month, day and year of your birthday. - Year",Please enter your Locally Assigned Student ID Number (LASID).,What grade are you in?,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-cure-q1,s-cure-q2,s-cure-q3,s-cure-q4,s-sten-q1,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,s-grmi-q2,s-grmi-q3,s-grmi-q4,s-appa-q1,s-appa-q2,s-appa-q3,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,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-sust-q1,s-sust-q2,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,s-tint-q2,s-tint-q3,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,What is your gender? - Selected Choice,Q90_3_TEXT,What is your race/ethnicity?(Please select all that apply) - Selected Choice,Q91_7_TEXT,Q_Language
|
||||||
3/30/2022 9:34,3/30/2022 9:44,0,100,598,1,2022-03-30T9:44:0,R_33pWjiebeWB2Cci,41.92610168,-71.30110168,anonymous,EN,1,160505,6,2,107,0,9,,,,3,3,3,3,1,3,1,,,,,3,3,3,3,,,,,,,,3,3,3,3,1,3,3,3,3,1,1,1,1,3,3,3,4,3,3,3,3,1,1,2,3,3,3,,,,,,,,,,2,4,4,4,3,3,3,plant,"1,5",,EN
|
3/30/2022 9:34,3/30/2022 9:44,0,100,598,1,2022-03-30T9:44:0,R_33pWjiebeWB2Cci,41.92610168,-71.30110168,anonymous,EN,1,160505,6,2,107,0,9,,,,3,3,3,3,1,3,1,,,,,3,3,3,3,,,,,,,,3,3,3,3,1,3,3,3,3,1,1,1,1,3,3,3,4,3,3,3,3,1,1,2,3,3,3,,,,,,,,,,2,4,4,4,3,3,3,plant,"1,5",,EN
|
||||||
3/30/2022 9:42,3/30/2022 10:19,0,100,2233,1,2022-03-30T10:19:0,R_11d0jVH4UESmBPB,41.92610168,-71.30110168,anonymous,EN,1,160505,1,15,105,193,12,3,3,4,3,3,3,1,2,3,3,4,4,3,4,5,5,4,4,3,2,3,3,2,2,2,,,,,,4,4,4,4,5,3,4,4,1,1,1,,,,,,,,,,,,5,4,3,3,4,3,2,4,3,,,,,,,1,,5,,EN
|
3/30/2022 9:42,3/30/2022 10:19,0,100,2233,1,2022-03-30T10:19:0,R_11d0jVH4UESmBPB,41.92610168,-71.30110168,anonymous,EN,1,160505,1,15,105,193,12,3,3,4,3,3,3,1,2,3,3,4,4,3,4,5,5,4,4,3,2,3,3,2,2,2,,,,,,4,4,4,4,5,3,4,4,1,1,1,,,,,,,,,,,,5,4,3,3,4,3,2,4,3,,,,,,,1,,5,,EN
|
||||||
3/15/2022 9:35,3/15/2022 9:46,0,100,621,1,2022-03-15T9:46:0,R_1eCPH5rELVj9luv,41.92610168,-71.30110168,anonymous,EN,1,160315,10,19,110,418,6,2,2,5,3,3,4,5,3,4,4,5,5,3,5,4,4,4,4,5,3,2,5,4,4,4,,,,,,,,,,,,,,,,,3,5,4,5,4,3,2,3,3,3,3,5,5,5,5,5,5,5,5,5,,,,,,,1,,3,,EN
|
3/15/2022 9:35,3/15/2022 9:46,0,100,621,1,2022-03-15T9:46:0,R_1eCPH5rELVj9luv,41.92610168,-71.30110168,anonymous,EN,1,160315,10,19,110,418,6,2,2,5,3,3,4,5,3,4,4,5,5,3,5,4,4,4,4,5,3,2,5,4,4,4,,,,,,,,,,,,,,,,,3,5,4,5,4,3,2,3,3,3,3,5,5,5,5,5,5,5,5,5,,,,,,,1,,3,,EN
|
||||||
|
|
|
||||||
|
Can't render this file because it is too large.
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
11
db/migrate/20220727231530_create_new_student.rb
Normal file
11
db/migrate/20220727231530_create_new_student.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
class CreateNewStudent < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :students do |t|
|
||||||
|
t.string :lasid
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :students, :lasid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddStudentToSurveyItemResponse < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_reference :survey_item_responses, :student, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20220728215528_add_response_id_to_student.rb
Normal file
5
db/migrate/20220728215528_add_response_id_to_student.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddResponseIdToStudent < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :students, :response_id, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
10
db/migrate/20220728232445_create_student_races.rb
Normal file
10
db/migrate/20220728232445_create_student_races.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
class CreateStudentRaces < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :student_races do |t|
|
||||||
|
t.references :student, null: false, foreign_key: true
|
||||||
|
t.references :race, null: false, foreign_key: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
24
db/schema.rb
24
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2022_07_22_030114) do
|
ActiveRecord::Schema[7.0].define(version: 2022_07_28_232445) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
@ -350,6 +350,23 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_22_030114) do
|
||||||
t.index ["dese_id"], name: "index_schools_on_dese_id", unique: true
|
t.index ["dese_id"], name: "index_schools_on_dese_id", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "student_races", force: :cascade do |t|
|
||||||
|
t.bigint "student_id", null: false
|
||||||
|
t.bigint "race_id", null: false
|
||||||
|
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 ["student_id"], name: "index_student_races_on_student_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "students", force: :cascade do |t|
|
||||||
|
t.string "lasid"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.string "response_id"
|
||||||
|
t.index ["lasid"], name: "index_students_on_lasid"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "subcategories", id: :serial, force: :cascade do |t|
|
create_table "subcategories", id: :serial, force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.integer "category_id"
|
t.integer "category_id"
|
||||||
|
|
@ -368,9 +385,11 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_22_030114) do
|
||||||
t.integer "academic_year_id", null: false
|
t.integer "academic_year_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.bigint "student_id"
|
||||||
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
|
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
|
||||||
t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
|
t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
|
||||||
t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_id"
|
t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_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"
|
t.index ["survey_item_id"], name: "index_survey_item_responses_on_survey_item_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -416,9 +435,12 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_22_030114) do
|
||||||
add_foreign_key "response_rates", "subcategories"
|
add_foreign_key "response_rates", "subcategories"
|
||||||
add_foreign_key "scales", "measures"
|
add_foreign_key "scales", "measures"
|
||||||
add_foreign_key "schools", "districts"
|
add_foreign_key "schools", "districts"
|
||||||
|
add_foreign_key "student_races", "races"
|
||||||
|
add_foreign_key "student_races", "students"
|
||||||
add_foreign_key "subcategories", "categories"
|
add_foreign_key "subcategories", "categories"
|
||||||
add_foreign_key "survey_item_responses", "academic_years"
|
add_foreign_key "survey_item_responses", "academic_years"
|
||||||
add_foreign_key "survey_item_responses", "schools"
|
add_foreign_key "survey_item_responses", "schools"
|
||||||
|
add_foreign_key "survey_item_responses", "students"
|
||||||
add_foreign_key "survey_item_responses", "survey_items"
|
add_foreign_key "survey_item_responses", "survey_items"
|
||||||
add_foreign_key "survey_items", "scales"
|
add_foreign_key "survey_items", "scales"
|
||||||
add_foreign_key "surveys", "academic_years"
|
add_foreign_key "surveys", "academic_years"
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,20 @@ namespace :data do
|
||||||
end
|
end
|
||||||
puts "=====================> Completed loading #{AdminDataValue.count} survey responses"
|
puts "=====================> Completed loading #{AdminDataValue.count} survey responses"
|
||||||
end
|
end
|
||||||
|
desc 'load students'
|
||||||
|
task load_students: :environment do
|
||||||
|
files = [ '2021-22_winchester_student_survey_responses.csv' ]
|
||||||
|
# files = ['2021-22_attleboro_student_survey_responses.csv',
|
||||||
|
# '2021-22_lowell_milford_student_survey_responses.csv',
|
||||||
|
# '2021-22_revere_somerville_wareham_student_survey_responses.csv',
|
||||||
|
# '2021-22_winchester_student_survey_responses.csv' ]
|
||||||
|
files.each do |file|
|
||||||
|
file = Rails.root.join('data', 'survey_responses', file)
|
||||||
|
puts "=====================> Loading student data from csv at path: #{file}"
|
||||||
|
StudentLoader.load_data filepath: file
|
||||||
|
end
|
||||||
|
puts "=====================> Completed loading #{Student.count} survey responses"
|
||||||
|
end
|
||||||
|
|
||||||
desc 'reset all cache counters'
|
desc 'reset all cache counters'
|
||||||
task reset_cache_counters: :environment do
|
task reset_cache_counters: :environment do
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
|
factory :student_race do
|
||||||
|
student { nil }
|
||||||
|
race { nil }
|
||||||
|
end
|
||||||
|
|
||||||
factory :race do
|
factory :race do
|
||||||
designation { "MyString" }
|
designation { "MyString" }
|
||||||
qualtrics_code { 1 }
|
qualtrics_code { 1 }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,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-q2,s-phys-q3,s-phys-q4,s-vale-q1,#N/A,#N/A,s-vale-q4,#N/A,s-acst-q2,s-acst-q3,#N/A,#N/A,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
|
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-q2,s-phys-q3,s-phys-q4,s-vale-q1,#N/A,#N/A,s-vale-q4,#N/A,s-acst-q2,s-acst-q3,#N/A,#N/A,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,What is your race/ethnicity?(Please select all that apply) - Selected Choice
|
||||||
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,,,,,,,anonymous,EN,1,8,160505,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,
|
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,160505,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,,1
|
||||||
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,,,,,,,anonymous,EN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,NA,,,,,,,,,,,,,,,,,,,,,EN,,,,
|
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"
|
||||||
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,,,,,42.53340149,-70.96530151,anonymous,EN,3,2,1600310,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,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,,,
|
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,1600310,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,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
|
||||||
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,,,,,42.63510132,-71.30139923,anonymous,EN,3,2,1600310,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
|
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,1600310,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
|
||||||
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,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1600310,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,
|
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,1600310,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"
|
||||||
|
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,1600310,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"
|
||||||
|
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,1600310,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,,
|
||||||
|
|
|
||||||
|
5
spec/models/student_race_spec.rb
Normal file
5
spec/models/student_race_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe StudentRace, type: :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
74
spec/services/student_loader_spec.rb
Normal file
74
spec/services/student_loader_spec.rb
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe StudentLoader do
|
||||||
|
let(:path_to_student_responses) { Rails.root.join('spec', 'fixtures', 'test_2020-21_student_survey_responses.csv') }
|
||||||
|
let(:american_indian) { Race.find_by_qualtrics_code(1) }
|
||||||
|
let(:asian) { Race.find_by_qualtrics_code(2) }
|
||||||
|
let(:black) { Race.find_by_qualtrics_code(3) }
|
||||||
|
let(:latinx) { Race.find_by_qualtrics_code(4) }
|
||||||
|
let(:white) { Race.find_by_qualtrics_code(5) }
|
||||||
|
let(:middle_eastern) { Race.find_by_qualtrics_code(8) }
|
||||||
|
let(:unknown) { Race.find_by_qualtrics_code(99) }
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
Rails.application.load_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
after :each do
|
||||||
|
DatabaseCleaner.clean
|
||||||
|
end
|
||||||
|
describe '#process_races' do
|
||||||
|
context 'as a standalone function' do
|
||||||
|
it 'race codes of 6 or 7 get classified as an unknown race' do
|
||||||
|
codes = [6]
|
||||||
|
expect(StudentLoader.process_races(codes:)).to eq Set[unknown]
|
||||||
|
codes = [7]
|
||||||
|
expect(StudentLoader.process_races(codes:)).to eq Set[unknown]
|
||||||
|
codes = [6, 7]
|
||||||
|
expect(StudentLoader.process_races(codes:)).to eq Set[unknown]
|
||||||
|
codes = [1, 6, 7]
|
||||||
|
expect(StudentLoader.process_races(codes:)).to eq Set[american_indian]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'self.load_data' do
|
||||||
|
context 'student survey responses' do
|
||||||
|
before :each do
|
||||||
|
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
|
||||||
|
StudentLoader.load_data filepath: path_to_student_responses
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'ensures student responses load correctly' do
|
||||||
|
assigns_student_to_the_survey_item_responses
|
||||||
|
assigns_races_to_students
|
||||||
|
is_idempotent_for_students
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assigns_student_to_the_survey_item_responses
|
||||||
|
expect(SurveyItemResponse.find_by_response_id('student_survey_response_1').student).not_to eq nil
|
||||||
|
expect(SurveyItemResponse.find_by_response_id('student_survey_response_1').student).to eq Student.find_by_lasid('123456')
|
||||||
|
expect(SurveyItemResponse.find_by_response_id('student_survey_response_6').student).not_to eq nil
|
||||||
|
expect(SurveyItemResponse.find_by_response_id('student_survey_response_6').student).to eq Student.find_by_response_id('student_survey_response_6')
|
||||||
|
end
|
||||||
|
|
||||||
|
def assigns_races_to_students
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_1').races).to eq [american_indian]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_2').races).to eq [asian, black, latinx]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_3').races).to eq [unknown]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_4').races).to eq [unknown]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_5').races).to eq [american_indian, asian, black, latinx, white,
|
||||||
|
middle_eastern]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_6').races).to eq [american_indian, asian, black, latinx, white,
|
||||||
|
middle_eastern]
|
||||||
|
expect(Student.find_by_response_id('student_survey_response_7').races).to eq [unknown]
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_idempotent_for_students
|
||||||
|
number_of_students = Student.count
|
||||||
|
StudentLoader.load_data filepath: path_to_student_responses
|
||||||
|
expect(Student.count).to eq number_of_students
|
||||||
|
end
|
||||||
|
|
@ -132,8 +132,8 @@ def loads_student_survey_item_response_values
|
||||||
end
|
end
|
||||||
|
|
||||||
def student_survey_item_response_count_matches_expected
|
def student_survey_item_response_count_matches_expected
|
||||||
expect(SurveyItemResponse.where(survey_item: s_phys_q1).count).to eq 3
|
expect(SurveyItemResponse.where(survey_item: s_phys_q1).count).to eq 5
|
||||||
expect(SurveyItemResponse.where(survey_item: s_phys_q2).count).to eq 3
|
expect(SurveyItemResponse.where(survey_item: s_phys_q2).count).to eq 5
|
||||||
end
|
end
|
||||||
|
|
||||||
def captures_likert_scores_for_student_survey_item_responses
|
def captures_likert_scores_for_student_survey_item_responses
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue