Refactor code based on rubocop and reek suggestions

pull/1/head
rebuilt 3 years ago
parent 1737122c80
commit bb5f668497

@ -2,3 +2,5 @@
detectors: detectors:
InstanceVariableAssumption: InstanceVariableAssumption:
enabled: false enabled: false
IrresponsibleModule:
enabled: false

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AnalyzeController < SqmApplicationController class AnalyzeController < SqmApplicationController
def index def index
assign_categories assign_categories
@ -19,7 +21,7 @@ class AnalyzeController < SqmApplicationController
end end
def assign_measures def assign_measures
@measures = @subcategory.measures.order(:measure_id).includes(%i[scales admin_data_items subcategory]) @measures = @subcategory.measures.order(:measure_id).includes(%i[subcategory])
end end
def assign_academic_years def assign_academic_years

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
before_action :set_google_analytics_id before_action :set_google_analytics_id
before_action :set_hotjar_id before_action :set_hotjar_id

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CategoriesController < SqmApplicationController class CategoriesController < SqmApplicationController
helper GaugeHelper helper GaugeHelper
def show def show

@ -1,3 +1,5 @@
# frozen_string_literal: true
class HomeController < ApplicationController class HomeController < ApplicationController
helper HeaderHelper helper HeaderHelper
def index def index

@ -1,3 +1,5 @@
# frozen_string_literal: true
class OverviewController < SqmApplicationController class OverviewController < SqmApplicationController
before_action :check_empty_dataset, only: [:index] before_action :check_empty_dataset, only: [:index]
helper VarianceHelper helper VarianceHelper
@ -16,7 +18,7 @@ class OverviewController < SqmApplicationController
end end
def check_empty_dataset def check_empty_dataset
@has_empty_dataset = !subcategories.any? do |subcategory| @has_empty_dataset = subcategories.none? do |subcategory|
response_rate = subcategory.response_rate(school: @school, academic_year: @academic_year) response_rate = subcategory.response_rate(school: @school, academic_year: @academic_year)
response_rate.meets_student_threshold || response_rate.meets_teacher_threshold response_rate.meets_student_threshold || response_rate.meets_teacher_threshold
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SqmApplicationController < ApplicationController class SqmApplicationController < ApplicationController
protect_from_forgery with: :exception, prepend: true protect_from_forgery with: :exception, prepend: true
before_action :set_schools_and_districts before_action :set_schools_and_districts

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AnalyzeHelper module AnalyzeHelper
def zone_label_width def zone_label_width
15 15
@ -73,7 +75,7 @@ module AnalyzeHelper
def empty_dataset?(measures:, school:, academic_year:) def empty_dataset?(measures:, school:, academic_year:)
@empty_dataset ||= Hash.new do |memo, (school, academic_year)| @empty_dataset ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = !measures.any? do |measure| memo[[school, academic_year]] = measures.none? do |measure|
response_rate = measure.subcategory.response_rate(school:, academic_year:) response_rate = measure.subcategory.response_rate(school:, academic_year:)
response_rate.meets_student_threshold || response_rate.meets_teacher_threshold response_rate.meets_student_threshold || response_rate.meets_teacher_threshold
end end

@ -1,2 +0,0 @@
module ColorHelper
end

@ -46,7 +46,7 @@ module HeaderHelper
true).joins('inner join academic_years a on response_rates.academic_year_id=a.id').order('a.range DESC').first true).joins('inner join academic_years a on response_rates.academic_year_id=a.id').order('a.range DESC').first
academic_year = latest_response_rate.academic_year if latest_response_rate.present? academic_year = latest_response_rate.academic_year if latest_response_rate.present?
academic_year ||= AcademicYear.order('range DESC').first academic_year || AcademicYear.order('range DESC').first
end end
def link_weight(path:) def link_weight(path:)

@ -1,3 +1,5 @@
# frozen_string_literal: true
module VarianceHelper module VarianceHelper
def heading_gutter def heading_gutter
30 30

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AcademicYear < ActiveRecord::Base class AcademicYear < ActiveRecord::Base
def self.find_by_date(date) def self.find_by_date(date)
if date.month > 6 if date.month > 6

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdminDataItem < ActiveRecord::Base class AdminDataItem < ActiveRecord::Base
belongs_to :scale belongs_to :scale
has_many :admin_data_values has_many :admin_data_values

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdminDataValue < ApplicationRecord class AdminDataValue < ApplicationRecord
belongs_to :school belongs_to :school
belongs_to :admin_data_item belongs_to :admin_data_item

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true self.abstract_class = true
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Category < ActiveRecord::Base class Category < ActiveRecord::Base
include FriendlyId include FriendlyId
friendly_id :name, use: [:slugged] friendly_id :name, use: [:slugged]

@ -1,2 +1,4 @@
# frozen_string_literal: true
class DataAvailability < Struct.new(:id, :description, :available?) class DataAvailability < Struct.new(:id, :description, :available?)
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class District < ApplicationRecord class District < ApplicationRecord
has_many :schools has_many :schools

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Legacy module Legacy
def self.table_name_prefix def self.table_name_prefix
'legacy_' 'legacy_'

@ -62,6 +62,8 @@ class Measure < ActiveRecord::Base
end end
def score(school:, academic_year:) def score(school:, academic_year:)
# average = SurveyItemResponse.where(school:, academic_year:).first
# Score.new(average, false, false, false)
@score ||= Hash.new do |memo, (school, academic_year)| @score ||= Hash.new do |memo, (school, academic_year)|
next Score::NIL_SCORE if incalculable_score(school:, academic_year:) next Score::NIL_SCORE if incalculable_score(school:, academic_year:)
@ -197,25 +199,33 @@ class Measure < ActiveRecord::Base
end end
def no_student_responses_exist?(school:, academic_year:) def no_student_responses_exist?(school:, academic_year:)
student_survey_items_by_survey_type(school:, @no_student_responses_exist ||= Hash.new do |memo, (school, academic_year)|
academic_year:).all? do |survey_item| memo[[school, academic_year]] = student_survey_items_by_survey_type(school:, academic_year:).all? do |survey_item|
survey_item.survey_item_responses.where(school:, survey_item.survey_item_responses.where(school:, academic_year:).none?
academic_year:).none? end
end end
@no_student_responses_exist[[school, academic_year]]
end end
def no_teacher_responses_exist?(school:, academic_year:) def no_teacher_responses_exist?(school:, academic_year:)
teacher_survey_items.all? do |survey_item| @no_teacher_responses_exist ||= Hash.new do |memo, (school, academic_year)|
survey_item.survey_item_responses.where(school:, memo[[school, academic_year]] = teacher_survey_items.all? do |survey_item|
academic_year:).none? survey_item.survey_item_responses.where(school:,
academic_year:).none?
end
end end
@no_teacher_responses_exist[[school, academic_year]]
end end
def incalculable_score(school:, academic_year:) def incalculable_score(school:, academic_year:)
meets_student_threshold = sufficient_student_data?(school:, academic_year:) @incalculable_score ||= Hash.new do |memo, (school, academic_year)|
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:) meets_student_threshold = sufficient_student_data?(school:, academic_year:)
lacks_sufficient_survey_data = !meets_student_threshold && !meets_teacher_threshold meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
lacks_sufficient_survey_data && !includes_admin_data_items? lacks_sufficient_survey_data = !meets_student_threshold && !meets_teacher_threshold
memo[[school, academic_year]] = lacks_sufficient_survey_data && !includes_admin_data_items?
end
@incalculable_score[[school, academic_year]]
end end
def collect_averages_for_teacher_student_and_admin_data(school:, academic_year:) def collect_averages_for_teacher_student_and_admin_data(school:, academic_year:)
@ -227,15 +237,26 @@ class Measure < ActiveRecord::Base
end end
def teacher_average(school:, academic_year:) def teacher_average(school:, academic_year:)
collect_survey_item_average(survey_items: teacher_survey_items, school:, academic_year:) @teacher_average ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] =
collect_survey_item_average(survey_items: teacher_survey_items, school:, academic_year:)
end
@teacher_average[[school, academic_year]]
end end
def student_average(school:, academic_year:) def student_average(school:, academic_year:)
collect_survey_item_average(survey_items: student_survey_items_by_survey_type(school:, academic_year:), school:, @student_average ||= Hash.new do |memo, (school, academic_year)|
academic_year:) memo[[school, academic_year]] = collect_survey_item_average(survey_items: student_survey_items_by_survey_type(school:, academic_year:), school:,
academic_year:)
end
@student_average[[school, academic_year]]
end end
def admin_data_averages(school:, academic_year:) def admin_data_averages(school:, academic_year:)
collect_admin_scale_average(admin_data_items:, school:, academic_year:) @admin_data_averages ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = collect_admin_scale_average(admin_data_items:, school:, academic_year:)
end
@admin_data_averages[[school, academic_year]]
end end
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Respondent < ApplicationRecord class Respondent < ApplicationRecord
belongs_to :school belongs_to :school
belongs_to :academic_year belongs_to :academic_year

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ResponseRate < ApplicationRecord class ResponseRate < ApplicationRecord
belongs_to :subcategory belongs_to :subcategory
belongs_to :school belongs_to :school

@ -1,3 +1,5 @@
# frozen_string_literal: true
module ResponseRateCalculator module ResponseRateCalculator
TEACHER_RATE_THRESHOLD = 25 TEACHER_RATE_THRESHOLD = 25
STUDENT_RATE_THRESHOLD = 25 STUDENT_RATE_THRESHOLD = 25
@ -19,7 +21,7 @@ module ResponseRateCalculator
return 0 unless total_possible_responses.positive? return 0 unless total_possible_responses.positive?
response_rate = (average_responses_per_survey_item / total_possible_responses.to_f * 100).round response_rate = (average_responses_per_survey_item / total_possible_responses.to_f * 100).round
cap_at_100(response_rate) cap_at_one_hundred(response_rate)
end end
def meets_student_threshold? def meets_student_threshold?
@ -32,7 +34,7 @@ module ResponseRateCalculator
private private
def cap_at_100(response_rate) def cap_at_one_hundred(response_rate)
response_rate > 100 ? 100 : response_rate response_rate > 100 ? 100 : response_rate
end end
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Scale < ApplicationRecord class Scale < ApplicationRecord
belongs_to :measure, counter_cache: true belongs_to :measure, counter_cache: true
has_many :survey_items has_many :survey_items
@ -38,8 +40,9 @@ class Scale < ApplicationRecord
def student_survey_items(school:, academic_year:) def student_survey_items(school:, academic_year:)
survey = Survey.where(school:, academic_year:).first survey = Survey.where(school:, academic_year:).first
return survey_items.student_survey_items.short_form_items if survey.present? && survey.form == 'short' student_survey_items = survey_items.student_survey_items
return student_survey_items.short_form_items if survey.present? && survey.form == 'short'
survey_items.student_survey_items student_survey_items
end end
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class School < ApplicationRecord class School < ApplicationRecord
belongs_to :district belongs_to :district

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Score < Struct.new(:average, :meets_teacher_threshold?, :meets_student_threshold?, :meets_admin_data_threshold?) class Score < Struct.new(:average, :meets_teacher_threshold?, :meets_student_threshold?, :meets_admin_data_threshold?)
NIL_SCORE = Score.new(nil, false, false, false) NIL_SCORE = Score.new(nil, false, false, false)
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class StudentResponseRateCalculator class StudentResponseRateCalculator
include ResponseRateCalculator include ResponseRateCalculator

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Subcategory < ActiveRecord::Base class Subcategory < ActiveRecord::Base
belongs_to :category, counter_cache: true belongs_to :category, counter_cache: true

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Survey < ApplicationRecord class Survey < ApplicationRecord
belongs_to :academic_year belongs_to :academic_year
belongs_to :school belongs_to :school

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SurveyItem < ActiveRecord::Base class SurveyItem < ActiveRecord::Base
belongs_to :scale, counter_cache: true belongs_to :scale, counter_cache: true
has_one :measure, through: :scale has_one :measure, through: :scale

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SurveyItemResponse < ActiveRecord::Base class SurveyItemResponse < ActiveRecord::Base
TEACHER_RESPONSE_THRESHOLD = 2 TEACHER_RESPONSE_THRESHOLD = 2
STUDENT_RESPONSE_THRESHOLD = 2 STUDENT_RESPONSE_THRESHOLD = 2

@ -1,3 +1,5 @@
# frozen_string_literal: true
class TeacherResponseRateCalculator class TeacherResponseRateCalculator
include ResponseRateCalculator include ResponseRateCalculator

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdminDataPresenter < DataItemPresenter class AdminDataPresenter < DataItemPresenter
def initialize(measure_id:, admin_data_items:, has_sufficient_data:, school:, academic_year:) def initialize(measure_id:, admin_data_items:, has_sufficient_data:, school:, academic_year:)
super(measure_id:, has_sufficient_data:, school:, academic_year:) super(measure_id:, has_sufficient_data:, school:, academic_year:)

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AnalyzeBarPresenter class AnalyzeBarPresenter
include AnalyzeHelper include AnalyzeHelper
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
@ -45,7 +47,8 @@ class AnalyzeBarPresenter
end end
def percentage def percentage
(score.average - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark) low_benchmark = zone.low_benchmark
(score.average - low_benchmark) / (zone.high_benchmark - low_benchmark)
end end
def zone def zone
@ -59,9 +62,10 @@ class AnalyzeBarPresenter
end end
def average def average
return 0 if score.average.nil? average = score.average
return 0 if average.nil?
score.average.round(6) average.round(6)
end end
private private

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CategoryPresenter class CategoryPresenter
def initialize(category:) def initialize(category:)
@category = category @category = category
@ -24,34 +26,12 @@ class CategoryPresenter
end end
def icon_class def icon_class
icon_suffix = case name icon_suffix = classes[name.to_sym]
when 'Teachers & Leadership'
'apple-alt'
when 'School Culture'
'school'
when 'Resources'
'users-cog'
when 'Academic Learning'
'graduation-cap'
when 'Community & Wellbeing'
'heart'
end
"fas fa-#{icon_suffix}" "fas fa-#{icon_suffix}"
end end
def icon_color_class def icon_color_class
color_suffix = case name color_suffix = colors[name.to_sym]
when 'Teachers & Leadership'
'blue'
when 'School Culture'
'red'
when 'Resources'
'black'
when 'Academic Learning'
'lime'
when 'Community & Wellbeing'
'teal'
end
"color-#{color_suffix}" "color-#{color_suffix}"
end end
@ -68,4 +48,22 @@ class CategoryPresenter
def to_model def to_model
@category @category
end end
private
def colors
{ 'Teachers & Leadership': 'blue',
'School Culture': 'red',
'Resources': 'black',
'Academic Learning': 'lime',
'Community & Wellbeing': 'teal' }
end
def classes
{ 'Teachers & Leadership': 'apple-alt',
'School Culture': 'school',
'Resources': 'users-cog',
'Academic Learning': 'graduation-cap',
'Community & Wellbeing': 'heart' }
end
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DataItemPresenter class DataItemPresenter
attr_reader :measure_id, :has_sufficient_data, :school, :academic_year attr_reader :measure_id, :has_sufficient_data, :school, :academic_year

@ -1,3 +1,5 @@
# frozen_string_literal: true
class GaugePresenter class GaugePresenter
def initialize(zones:, score:) def initialize(zones:, score:)
@zones = zones @zones = zones

@ -1,6 +1,7 @@
# frozen_string_literal: true
class GroupedBarColumnPresenter class GroupedBarColumnPresenter
include AnalyzeHelper include AnalyzeHelper
include ColorHelper
attr_reader :measure_name, :measure_id, :category, :position, :measure, :school, :academic_years attr_reader :measure_name, :measure_id, :category, :position, :measure, :school, :academic_years
@ -86,8 +87,8 @@ class GroupedBarColumnPresenter
[year, score(index)] [year, score(index)]
end end
yearly_scores.reject do |yearly_score| yearly_scores.reject do |yearly_score|
score = yearly_score[1] average = yearly_score[1].average
score.average.nil? || score.average.zero? || score.average.nan? average.nil? || average.zero? || average.nan?
end end
end end
@ -97,6 +98,7 @@ class GroupedBarColumnPresenter
end end
def bar_x(index) def bar_x(index)
column_start_x + (index * bar_width * 1.2) + ((column_end_x - column_start_x) - (yearly_scores.size * bar_width * 1.2)) / 2 column_start_x + (index * bar_width * 1.2) +
((column_end_x - column_start_x) - (yearly_scores.size * bar_width * 1.2)) / 2
end end
end end

@ -1,22 +1,15 @@
# frozen_string_literal: true
class MeasurePresenter class MeasurePresenter
attr_reader :measure, :academic_year, :school attr_reader :measure, :academic_year, :school, :id, :name, :description
def initialize(measure:, academic_year:, school:) def initialize(measure:, academic_year:, school:)
@measure = measure @measure = measure
@academic_year = academic_year @academic_year = academic_year
@school = school @school = school
end @id = measure_id
@name = measure.name
def id @description = measure.description
@measure.measure_id
end
def name
@measure.name
end
def description
@measure.description
end end
def gauge_presenter def gauge_presenter
@ -29,27 +22,49 @@ class MeasurePresenter
def data_item_presenters def data_item_presenters
[].tap do |array| [].tap do |array|
if @measure.student_survey_items.any? array << student_survey_presenter if student_survey_items.any?
array << StudentSurveyPresenter.new(measure_id: @measure.measure_id, survey_items: @measure.student_survey_items, array << teacher_survey_presenter if teacher_survey_items.any?
has_sufficient_data: score_for_measure.meets_student_threshold?, school:, academic_year:) array << admin_data_presenter if admin_data_items.any?
end
if @measure.teacher_survey_items.any?
array << TeacherSurveyPresenter.new(measure_id: @measure.measure_id, survey_items: @measure.teacher_survey_items,
has_sufficient_data: score_for_measure.meets_teacher_threshold?, school:, academic_year:)
end
if @measure.admin_data_items.any?
array << AdminDataPresenter.new(measure_id: @measure.measure_id,
admin_data_items: @measure.admin_data_items, has_sufficient_data: score_for_measure.meets_admin_data_threshold?, school:, academic_year:)
end
end end
end end
private private
def admin_data_items
measure.admin_data_items
end
def score_for_measure def score_for_measure
@score ||= @measure.score(school: @school, academic_year: @academic_year) @score ||= @measure.score(school: @school, academic_year: @academic_year)
end end
def student_survey_items
measure.student_survey_items
end
def teacher_survey_items
measure.teacher_survey_items
end
def measure_id
measure.measure_id
end
def student_survey_presenter
StudentSurveyPresenter.new(measure_id:, survey_items: student_survey_items,
has_sufficient_data: score_for_measure.meets_student_threshold?, school:, academic_year:)
end
def teacher_survey_presenter
TeacherSurveyPresenter.new(measure_id:, survey_items: teacher_survey_items,
has_sufficient_data: score_for_measure.meets_teacher_threshold?, school:, academic_year:)
end
def admin_data_presenter
AdminDataPresenter.new(measure_id:,
admin_data_items:, has_sufficient_data: score_for_measure.meets_admin_data_threshold?, school:, academic_year:)
end
def zones def zones
Zones.new( Zones.new(
watch_low_benchmark: @measure.watch_low_benchmark, watch_low_benchmark: @measure.watch_low_benchmark,

@ -1,3 +1,5 @@
# frozen_string_literal: true
class StudentGroupedBarColumnPresenter < GroupedBarColumnPresenter class StudentGroupedBarColumnPresenter < GroupedBarColumnPresenter
def label def label
'All Students' 'All Students'

@ -1,3 +1,5 @@
# frozen_string_literal: true
class StudentSurveyPresenter < DataItemPresenter class StudentSurveyPresenter < DataItemPresenter
attr_reader :survey_items attr_reader :survey_items

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SubcategoryCardPresenter class SubcategoryCardPresenter
attr_reader :name, :subcategory, :category, :subcategory_id attr_reader :name, :subcategory, :category, :subcategory_id

@ -1,20 +1,15 @@
# frozen_string_literal: true
class SubcategoryPresenter class SubcategoryPresenter
attr_reader :subcategory, :academic_year, :school, :id, :name, :description
def initialize(subcategory:, academic_year:, school:) def initialize(subcategory:, academic_year:, school:)
@subcategory = subcategory @subcategory = subcategory
@academic_year = academic_year @academic_year = academic_year
@school = school @school = school
end @id = @subcategory.subcategory_id
@name = @subcategory.name
def id @description = @subcategory.description
@subcategory.subcategory_id
end
def name
@subcategory.name
end
def description
@subcategory.description
end end
def gauge_presenter def gauge_presenter
@ -65,9 +60,10 @@ class SubcategoryPresenter
end end
def admin_data_item_count def admin_data_item_count
return AdminDataItem.for_measures(@subcategory.measures).count if @school.is_hs measures = @subcategory.measures
return AdminDataItem.for_measures(measures).count if @school.is_hs
AdminDataItem.non_hs_items_for_measures(@subcategory.measures).count AdminDataItem.non_hs_items_for_measures(measures).count
end end
def format_a_non_applicable_rate(rate) def format_a_non_applicable_rate(rate)

@ -1,3 +1,5 @@
# frozen_string_literal: true
class TeacherGroupedBarColumnPresenter < GroupedBarColumnPresenter class TeacherGroupedBarColumnPresenter < GroupedBarColumnPresenter
def label def label
'All Teachers' 'All Teachers'

@ -1,3 +1,5 @@
# frozen_string_literal: true
class TeacherSurveyPresenter < DataItemPresenter class TeacherSurveyPresenter < DataItemPresenter
attr_reader :survey_items attr_reader :survey_items

@ -1,3 +1,5 @@
# frozen_string_literal: true
class VarianceChartRowPresenter class VarianceChartRowPresenter
include Comparable include Comparable

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Zones class Zones
def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:) def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:)
@watch_low_benchmark = watch_low_benchmark @watch_low_benchmark = watch_low_benchmark
@ -7,7 +9,19 @@ class Zones
@warning_low_benchmark = 1 @warning_low_benchmark = 1
end end
Zone = Struct.new(:low_benchmark, :high_benchmark, :type) Zone = Struct.new(:low_benchmark, :high_benchmark, :type) do
def contains?(number)
return false if number.nil? || number.is_a?(Float) && number.nan?
number.between?(low_benchmark, high_benchmark)
end
end
def all_zones
[
ideal_zone, approval_zone, growth_zone, watch_zone, warning_zone, insufficient_data
]
end
def warning_zone def warning_zone
Zone.new(1, @watch_low_benchmark, :warning) Zone.new(1, @watch_low_benchmark, :warning)
@ -30,26 +44,10 @@ class Zones
end end
def insufficient_data def insufficient_data
Zone.new(0, @warning_low_benchmark, :insufficient_data) Zone.new(Float::MIN, Float::MAX, :insufficient_data)
end end
def zone_for_score(score) def zone_for_score(score)
return insufficient_data if score.nil? all_zones.find { |zone| zone.contains?(score) } || insufficient_data
return insufficient_data if score.is_a?(Float) && score.nan?
case score
when ideal_zone.low_benchmark..ideal_zone.high_benchmark
ideal_zone
when approval_zone.low_benchmark..approval_zone.high_benchmark
approval_zone
when growth_zone.low_benchmark..growth_zone.high_benchmark
growth_zone
when watch_zone.low_benchmark..watch_zone.high_benchmark
watch_zone
when 1..warning_zone.high_benchmark
warning_zone
else
insufficient_data
end
end end
end end

@ -1,26 +1,18 @@
# frozen_string_literal: true
require 'csv' require 'csv'
class AdminDataLoader class AdminDataLoader
def self.load_data(filepath:) def self.load_data(filepath:)
CSV.parse(File.read(filepath), headers: true) do |row| CSV.parse(File.read(filepath), headers: true) do |row|
likert_score = row['LikertScore'] || row['Likert Score'] || row['Likert_Score'] score = likert_score(row:)
likert_score = likert_score.to_f unless valid_likert_score(likert_score: score)
likert_score = 1 if likert_score > 0 && likert_score < 1 puts "Invalid score: #{score}
for school: #{School.find_by_dese_id(row['DESE ID']).name}
unless valid_likert_score(likert_score:) admin data item #{admin_data_item(row:)} "
puts "This value is not valid #{likert_score}"
next next
end end
create_admin_data_value(row:, score:)
ay = row['Academic Year'] || row['AcademicYear']
dese_id = row['DESE ID'] || row['Dese ID'] || row['Dese Id']
admin_data_item_id = row['Item ID'] || row['Item Id']
admin_data_value = AdminDataValue.new
admin_data_value.likert_score = likert_score
admin_data_value.academic_year = AcademicYear.find_by_range ay
admin_data_value.school = School.find_by_dese_id dese_id.to_i
admin_data_value.admin_data_item = AdminDataItem.find_by_admin_data_item_id admin_data_item_id
admin_data_value.save!
end end
end end
@ -28,5 +20,33 @@ class AdminDataLoader
likert_score >= 1 && likert_score <= 5 likert_score >= 1 && likert_score <= 5
end end
def self.likert_score(row:)
likert_score = row['LikertScore'] || row['Likert Score'] || row['Likert_Score']
likert_score = likert_score.to_f
likert_score = 1 if likert_score.positive? && likert_score < 1
likert_score
end
def self.ay(row:)
row['Academic Year'] || row['AcademicYear']
end
def self.dese_id(row:)
row['DESE ID'] || row['Dese ID'] || row['Dese Id']
end
def self.admin_data_item(row:)
row['Item ID'] || row['Item Id']
end
def self.create_admin_data_value(row:, score:)
admin_data_value = AdminDataValue.new
admin_data_value.likert_score = score
admin_data_value.academic_year = AcademicYear.find_by_range ay(row:)
admin_data_value.school = School.find_by_dese_id dese_id(row:).to_i
admin_data_value.admin_data_item = AdminDataItem.find_by_admin_data_item_id admin_data_item(row:)
admin_data_value.save!
end
private_class_method :valid_likert_score private_class_method :valid_likert_score
end end

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ResponseRateLoader class ResponseRateLoader
def self.reset(schools: School.all, academic_years: AcademicYear.all, subcategories: Subcategory.all) def self.reset(schools: School.all, academic_years: AcademicYear.all, subcategories: Subcategory.all)
milford = School.find_by_slug 'milford-high-school' milford = School.find_by_slug 'milford-high-school'

@ -1,10 +1,12 @@
# frozen_string_literal: true
require 'csv' require 'csv'
class SurveyResponsesDataLoader class SurveyResponsesDataLoader
def self.load_data(filepath:) def self.load_data(filepath:)
File.open(filepath) do |file| File.open(filepath) do |file|
headers = file.first headers = file.first
survey_items = SurveyItem.where(survey_item_id: get_survey_item_ids_from_headers(file:, headers:)) survey_items = SurveyItem.where(survey_item_id: get_survey_item_ids_from_headers(headers:))
file.lazy.each_slice(1000) do |lines| file.lazy.each_slice(1000) do |lines|
survey_item_responses = CSV.parse(lines.join, headers:).map do |row| survey_item_responses = CSV.parse(lines.join, headers:).map do |row|
@ -19,43 +21,42 @@ class SurveyResponsesDataLoader
private private
def self.process_row(row:, survey_items:) def self.process_row(row:, survey_items:)
return unless dese_id?(row['DESE ID']) id = dese_id(row)
return unless dese_id?(id)
school = School.find_by_dese_id(row['DESE ID']) school = School.find_by_dese_id(id)
return unless school.present? return unless school.present?
process_survey_items(row:, survey_items:, school:) process_survey_items(row:, survey_items:, school:)
end end
def self.process_survey_items(row:, survey_items:, school:) def self.process_survey_items(row:, survey_items:, school:)
response_id = row['Response ID'] || row['ResponseId'] || row['ResponseID'] id = response_id(row)
survey_items.map do |survey_item| survey_items.map do |survey_item|
likert_score = row[survey_item.survey_item_id] likert_score = row[survey_item.survey_item_id] || next
next if likert_score.nil?
unless likert_score.valid_likert_score? unless likert_score.valid_likert_score?
puts "Response ID: #{response_id}, Likert score: #{likert_score} rejected" unless likert_score == 'NA' puts "Response ID: #{id}, Likert score: #{likert_score} rejected" unless likert_score == 'NA'
next next
end end
response = survey_item_response(response_id: id, survey_item:)
survey_item_response = SurveyItemResponse.where(response_id:, survey_item:).first create_or_update_response(survey_item_response: response, likert_score:, school:, row:, survey_item:)
create_or_update_survey_item_response(survey_item_response:, likert_score:, school:, response_id:, row:,
survey_item:)
end.compact end.compact
end end
def self.create_or_update_survey_item_response(survey_item_response:, likert_score:, school:, row:, survey_item:, response_id:) def self.create_or_update_response(survey_item_response:, likert_score:, school:, row:, survey_item:)
if survey_item_response.present? if survey_item_response.present?
survey_item_response.update!(likert_score:) if survey_item_response.likert_score != likert_score survey_item_response.update!(likert_score:) if survey_item_response.likert_score != likert_score
[] []
else else
SurveyItemResponse.new(response_id:, academic_year: academic_year(row), school:, survey_item:, likert_score:) SurveyItemResponse.new(response_id: response_id(row), academic_year: academic_year(row), school:, survey_item:,
likert_score:)
end end
end end
def self.get_survey_item_ids_from_headers(file:, headers:) def self.get_survey_item_ids_from_headers(headers:)
CSV.parse(headers, headers: true).headers CSV.parse(headers, headers: true).headers
.filter { |header| header.present? } .filter(&:present?)
.filter { |header| header.start_with? 't-' or header.start_with? 's-' } .filter { |header| header.start_with? 't-' or header.start_with? 's-' }
end end
@ -71,10 +72,28 @@ class SurveyResponsesDataLoader
AcademicYear.find_by_date response_date(row) AcademicYear.find_by_date response_date(row)
end end
def self.survey_item_response(response_id:, survey_item:)
SurveyItemResponse.find_by(response_id:, survey_item:)
end
def self.response_id(row)
row['Response ID'] || row['ResponseId'] || row['ResponseID']
end
def self.dese_id(row)
row['DESE ID' || 'Dese ID'] || row['DeseId'] || row['DeseID']
end
private_class_method :process_row private_class_method :process_row
private_class_method :process_survey_items private_class_method :process_survey_items
private_class_method :get_survey_item_ids_from_headers private_class_method :get_survey_item_ids_from_headers
private_class_method :dese_id? private_class_method :dese_id?
private_class_method :create_or_update_response
private_class_method :response_date
private_class_method :academic_year
private_class_method :survey_item_response
private_class_method :response_id
private_class_method :dese_id
end end
module StringMonkeyPatches module StringMonkeyPatches

@ -64,7 +64,7 @@ describe AdminDataLoader do
describe 'output to console' do describe 'output to console' do
it 'outputs a messsage saying a value has been rejected' do it 'outputs a messsage saying a value has been rejected' do
output = capture_stdout { AdminDataLoader.load_data filepath: path_to_admin_data }.gsub("\n", '') output = capture_stdout { AdminDataLoader.load_data filepath: path_to_admin_data }.gsub("\n", '')
expect(output).to eq 'This value is not valid 0.0This value is not valid 100.0' expect(output).to eq 'Invalid score: 0.0 for school: Attleboro High School admin data item a-reso-i1 Invalid score: 100.0 for school: Winchester High School admin data item a-sust-i3 '
end end
end end
end end

Loading…
Cancel
Save