mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
Refactor code based on rubocop and reek suggestions
This commit is contained in:
parent
1737122c80
commit
bb5f668497
51 changed files with 296 additions and 146 deletions
|
|
@ -2,3 +2,5 @@
|
|||
detectors:
|
||||
InstanceVariableAssumption:
|
||||
enabled: false
|
||||
IrresponsibleModule:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AnalyzeController < SqmApplicationController
|
||||
def index
|
||||
assign_categories
|
||||
|
|
@ -19,7 +21,7 @@ class AnalyzeController < SqmApplicationController
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def assign_academic_years
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
before_action :set_google_analytics_id
|
||||
before_action :set_hotjar_id
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CategoriesController < SqmApplicationController
|
||||
helper GaugeHelper
|
||||
def show
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HomeController < ApplicationController
|
||||
helper HeaderHelper
|
||||
def index
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OverviewController < SqmApplicationController
|
||||
before_action :check_empty_dataset, only: [:index]
|
||||
helper VarianceHelper
|
||||
|
|
@ -16,7 +18,7 @@ class OverviewController < SqmApplicationController
|
|||
end
|
||||
|
||||
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.meets_student_threshold || response_rate.meets_teacher_threshold
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SqmApplicationController < ApplicationController
|
||||
protect_from_forgery with: :exception, prepend: true
|
||||
before_action :set_schools_and_districts
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AnalyzeHelper
|
||||
def zone_label_width
|
||||
15
|
||||
|
|
@ -73,7 +75,7 @@ module AnalyzeHelper
|
|||
|
||||
def empty_dataset?(measures:, 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.meets_student_threshold || response_rate.meets_teacher_threshold
|
||||
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
|
||||
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
|
||||
|
||||
def link_weight(path:)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module VarianceHelper
|
||||
def heading_gutter
|
||||
30
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AcademicYear < ActiveRecord::Base
|
||||
def self.find_by_date(date)
|
||||
if date.month > 6
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AdminDataItem < ActiveRecord::Base
|
||||
belongs_to :scale
|
||||
has_many :admin_data_values
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AdminDataValue < ApplicationRecord
|
||||
belongs_to :school
|
||||
belongs_to :admin_data_item
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Category < ActiveRecord::Base
|
||||
include FriendlyId
|
||||
friendly_id :name, use: [:slugged]
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DataAvailability < Struct.new(:id, :description, :available?)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class District < ApplicationRecord
|
||||
has_many :schools
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Legacy
|
||||
def self.table_name_prefix
|
||||
'legacy_'
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ class Measure < ActiveRecord::Base
|
|||
end
|
||||
|
||||
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)|
|
||||
next Score::NIL_SCORE if incalculable_score(school:, academic_year:)
|
||||
|
||||
|
|
@ -197,25 +199,33 @@ class Measure < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def no_student_responses_exist?(school:, academic_year:)
|
||||
student_survey_items_by_survey_type(school:,
|
||||
academic_year:).all? do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:,
|
||||
academic_year:).none?
|
||||
@no_student_responses_exist ||= Hash.new do |memo, (school, academic_year)|
|
||||
memo[[school, academic_year]] = student_survey_items_by_survey_type(school:, academic_year:).all? do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:, academic_year:).none?
|
||||
end
|
||||
end
|
||||
@no_student_responses_exist[[school, academic_year]]
|
||||
end
|
||||
|
||||
def no_teacher_responses_exist?(school:, academic_year:)
|
||||
teacher_survey_items.all? do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:,
|
||||
academic_year:).none?
|
||||
@no_teacher_responses_exist ||= Hash.new do |memo, (school, academic_year)|
|
||||
memo[[school, academic_year]] = teacher_survey_items.all? do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:,
|
||||
academic_year:).none?
|
||||
end
|
||||
end
|
||||
@no_teacher_responses_exist[[school, academic_year]]
|
||||
end
|
||||
|
||||
def incalculable_score(school:, academic_year:)
|
||||
meets_student_threshold = sufficient_student_data?(school:, academic_year:)
|
||||
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
|
||||
lacks_sufficient_survey_data = !meets_student_threshold && !meets_teacher_threshold
|
||||
lacks_sufficient_survey_data && !includes_admin_data_items?
|
||||
@incalculable_score ||= Hash.new do |memo, (school, academic_year)|
|
||||
meets_student_threshold = sufficient_student_data?(school:, academic_year:)
|
||||
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
|
||||
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
|
||||
|
||||
def collect_averages_for_teacher_student_and_admin_data(school:, academic_year:)
|
||||
|
|
@ -227,15 +237,26 @@ class Measure < ActiveRecord::Base
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def student_average(school:, academic_year:)
|
||||
collect_survey_item_average(survey_items: student_survey_items_by_survey_type(school:, academic_year:), school:,
|
||||
academic_year:)
|
||||
@student_average ||= Hash.new do |memo, (school, 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
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Respondent < ApplicationRecord
|
||||
belongs_to :school
|
||||
belongs_to :academic_year
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ResponseRate < ApplicationRecord
|
||||
belongs_to :subcategory
|
||||
belongs_to :school
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ResponseRateCalculator
|
||||
TEACHER_RATE_THRESHOLD = 25
|
||||
STUDENT_RATE_THRESHOLD = 25
|
||||
|
|
@ -19,7 +21,7 @@ module ResponseRateCalculator
|
|||
return 0 unless total_possible_responses.positive?
|
||||
|
||||
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
|
||||
|
||||
def meets_student_threshold?
|
||||
|
|
@ -32,7 +34,7 @@ module ResponseRateCalculator
|
|||
|
||||
private
|
||||
|
||||
def cap_at_100(response_rate)
|
||||
def cap_at_one_hundred(response_rate)
|
||||
response_rate > 100 ? 100 : response_rate
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Scale < ApplicationRecord
|
||||
belongs_to :measure, counter_cache: true
|
||||
has_many :survey_items
|
||||
|
|
@ -38,8 +40,9 @@ class Scale < ApplicationRecord
|
|||
|
||||
def student_survey_items(school:, academic_year:)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class School < ApplicationRecord
|
||||
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?)
|
||||
NIL_SCORE = Score.new(nil, false, false, false)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StudentResponseRateCalculator
|
||||
include ResponseRateCalculator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Subcategory < ActiveRecord::Base
|
||||
belongs_to :category, counter_cache: true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Survey < ApplicationRecord
|
||||
belongs_to :academic_year
|
||||
belongs_to :school
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SurveyItem < ActiveRecord::Base
|
||||
belongs_to :scale, counter_cache: true
|
||||
has_one :measure, through: :scale
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SurveyItemResponse < ActiveRecord::Base
|
||||
TEACHER_RESPONSE_THRESHOLD = 2
|
||||
STUDENT_RESPONSE_THRESHOLD = 2
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TeacherResponseRateCalculator
|
||||
include ResponseRateCalculator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AdminDataPresenter < DataItemPresenter
|
||||
def initialize(measure_id:, admin_data_items:, 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
|
||||
include AnalyzeHelper
|
||||
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
|
||||
|
|
@ -45,7 +47,8 @@ class AnalyzeBarPresenter
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def zone
|
||||
|
|
@ -59,9 +62,10 @@ class AnalyzeBarPresenter
|
|||
end
|
||||
|
||||
def average
|
||||
return 0 if score.average.nil?
|
||||
average = score.average
|
||||
return 0 if average.nil?
|
||||
|
||||
score.average.round(6)
|
||||
average.round(6)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CategoryPresenter
|
||||
def initialize(category:)
|
||||
@category = category
|
||||
|
|
@ -24,34 +26,12 @@ class CategoryPresenter
|
|||
end
|
||||
|
||||
def icon_class
|
||||
icon_suffix = case name
|
||||
when 'Teachers & Leadership'
|
||||
'apple-alt'
|
||||
when 'School Culture'
|
||||
'school'
|
||||
when 'Resources'
|
||||
'users-cog'
|
||||
when 'Academic Learning'
|
||||
'graduation-cap'
|
||||
when 'Community & Wellbeing'
|
||||
'heart'
|
||||
end
|
||||
icon_suffix = classes[name.to_sym]
|
||||
"fas fa-#{icon_suffix}"
|
||||
end
|
||||
|
||||
def icon_color_class
|
||||
color_suffix = case name
|
||||
when 'Teachers & Leadership'
|
||||
'blue'
|
||||
when 'School Culture'
|
||||
'red'
|
||||
when 'Resources'
|
||||
'black'
|
||||
when 'Academic Learning'
|
||||
'lime'
|
||||
when 'Community & Wellbeing'
|
||||
'teal'
|
||||
end
|
||||
color_suffix = colors[name.to_sym]
|
||||
"color-#{color_suffix}"
|
||||
end
|
||||
|
||||
|
|
@ -68,4 +48,22 @@ class CategoryPresenter
|
|||
def to_model
|
||||
@category
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DataItemPresenter
|
||||
attr_reader :measure_id, :has_sufficient_data, :school, :academic_year
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class GaugePresenter
|
||||
def initialize(zones:, score:)
|
||||
@zones = zones
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class GroupedBarColumnPresenter
|
||||
include AnalyzeHelper
|
||||
include ColorHelper
|
||||
|
||||
attr_reader :measure_name, :measure_id, :category, :position, :measure, :school, :academic_years
|
||||
|
||||
|
|
@ -86,8 +87,8 @@ class GroupedBarColumnPresenter
|
|||
[year, score(index)]
|
||||
end
|
||||
yearly_scores.reject do |yearly_score|
|
||||
score = yearly_score[1]
|
||||
score.average.nil? || score.average.zero? || score.average.nan?
|
||||
average = yearly_score[1].average
|
||||
average.nil? || average.zero? || average.nan?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -97,6 +98,7 @@ class GroupedBarColumnPresenter
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,22 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MeasurePresenter
|
||||
attr_reader :measure, :academic_year, :school
|
||||
attr_reader :measure, :academic_year, :school, :id, :name, :description
|
||||
|
||||
def initialize(measure:, academic_year:, school:)
|
||||
@measure = measure
|
||||
@academic_year = academic_year
|
||||
@school = school
|
||||
end
|
||||
|
||||
def id
|
||||
@measure.measure_id
|
||||
end
|
||||
|
||||
def name
|
||||
@measure.name
|
||||
end
|
||||
|
||||
def description
|
||||
@measure.description
|
||||
@id = measure_id
|
||||
@name = measure.name
|
||||
@description = measure.description
|
||||
end
|
||||
|
||||
def gauge_presenter
|
||||
|
|
@ -29,27 +22,49 @@ class MeasurePresenter
|
|||
|
||||
def data_item_presenters
|
||||
[].tap do |array|
|
||||
if @measure.student_survey_items.any?
|
||||
array << StudentSurveyPresenter.new(measure_id: @measure.measure_id, survey_items: @measure.student_survey_items,
|
||||
has_sufficient_data: score_for_measure.meets_student_threshold?, school:, academic_year:)
|
||||
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
|
||||
array << student_survey_presenter if student_survey_items.any?
|
||||
array << teacher_survey_presenter if teacher_survey_items.any?
|
||||
array << admin_data_presenter if admin_data_items.any?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def admin_data_items
|
||||
measure.admin_data_items
|
||||
end
|
||||
|
||||
def score_for_measure
|
||||
@score ||= @measure.score(school: @school, academic_year: @academic_year)
|
||||
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
|
||||
Zones.new(
|
||||
watch_low_benchmark: @measure.watch_low_benchmark,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StudentGroupedBarColumnPresenter < GroupedBarColumnPresenter
|
||||
def label
|
||||
'All Students'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StudentSurveyPresenter < DataItemPresenter
|
||||
attr_reader :survey_items
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SubcategoryCardPresenter
|
||||
attr_reader :name, :subcategory, :category, :subcategory_id
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SubcategoryPresenter
|
||||
attr_reader :subcategory, :academic_year, :school, :id, :name, :description
|
||||
|
||||
def initialize(subcategory:, academic_year:, school:)
|
||||
@subcategory = subcategory
|
||||
@academic_year = academic_year
|
||||
@school = school
|
||||
end
|
||||
|
||||
def id
|
||||
@subcategory.subcategory_id
|
||||
end
|
||||
|
||||
def name
|
||||
@subcategory.name
|
||||
end
|
||||
|
||||
def description
|
||||
@subcategory.description
|
||||
@id = @subcategory.subcategory_id
|
||||
@name = @subcategory.name
|
||||
@description = @subcategory.description
|
||||
end
|
||||
|
||||
def gauge_presenter
|
||||
|
|
@ -65,9 +60,10 @@ class SubcategoryPresenter
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def format_a_non_applicable_rate(rate)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TeacherGroupedBarColumnPresenter < GroupedBarColumnPresenter
|
||||
def label
|
||||
'All Teachers'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TeacherSurveyPresenter < DataItemPresenter
|
||||
attr_reader :survey_items
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class VarianceChartRowPresenter
|
||||
include Comparable
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Zones
|
||||
def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:)
|
||||
@watch_low_benchmark = watch_low_benchmark
|
||||
|
|
@ -7,7 +9,19 @@ class Zones
|
|||
@warning_low_benchmark = 1
|
||||
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
|
||||
Zone.new(1, @watch_low_benchmark, :warning)
|
||||
|
|
@ -30,26 +44,10 @@ class Zones
|
|||
end
|
||||
|
||||
def insufficient_data
|
||||
Zone.new(0, @warning_low_benchmark, :insufficient_data)
|
||||
Zone.new(Float::MIN, Float::MAX, :insufficient_data)
|
||||
end
|
||||
|
||||
def zone_for_score(score)
|
||||
return insufficient_data if score.nil?
|
||||
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
|
||||
all_zones.find { |zone| zone.contains?(score) } || insufficient_data
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,26 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
class AdminDataLoader
|
||||
def self.load_data(filepath:)
|
||||
CSV.parse(File.read(filepath), headers: true) do |row|
|
||||
likert_score = row['LikertScore'] || row['Likert Score'] || row['Likert_Score']
|
||||
likert_score = likert_score.to_f
|
||||
likert_score = 1 if likert_score > 0 && likert_score < 1
|
||||
|
||||
unless valid_likert_score(likert_score:)
|
||||
puts "This value is not valid #{likert_score}"
|
||||
score = likert_score(row:)
|
||||
unless valid_likert_score(likert_score: score)
|
||||
puts "Invalid score: #{score}
|
||||
for school: #{School.find_by_dese_id(row['DESE ID']).name}
|
||||
admin data item #{admin_data_item(row:)} "
|
||||
next
|
||||
end
|
||||
|
||||
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!
|
||||
create_admin_data_value(row:, score:)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -28,5 +20,33 @@ class AdminDataLoader
|
|||
likert_score >= 1 && likert_score <= 5
|
||||
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
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ResponseRateLoader
|
||||
def self.reset(schools: School.all, academic_years: AcademicYear.all, subcategories: Subcategory.all)
|
||||
milford = School.find_by_slug 'milford-high-school'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
class SurveyResponsesDataLoader
|
||||
def self.load_data(filepath:)
|
||||
File.open(filepath) do |file|
|
||||
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|
|
||||
survey_item_responses = CSV.parse(lines.join, headers:).map do |row|
|
||||
|
|
@ -19,43 +21,42 @@ class SurveyResponsesDataLoader
|
|||
private
|
||||
|
||||
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?
|
||||
|
||||
process_survey_items(row:, survey_items:, school:)
|
||||
end
|
||||
|
||||
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|
|
||||
likert_score = row[survey_item.survey_item_id]
|
||||
next if likert_score.nil?
|
||||
likert_score = row[survey_item.survey_item_id] || next
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
survey_item_response = SurveyItemResponse.where(response_id:, survey_item:).first
|
||||
create_or_update_survey_item_response(survey_item_response:, likert_score:, school:, response_id:, row:,
|
||||
survey_item:)
|
||||
response = survey_item_response(response_id: id, survey_item:)
|
||||
create_or_update_response(survey_item_response: response, likert_score:, school:, row:, survey_item:)
|
||||
end.compact
|
||||
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?
|
||||
survey_item_response.update!(likert_score:) if survey_item_response.likert_score != likert_score
|
||||
[]
|
||||
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
|
||||
|
||||
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
|
||||
.filter { |header| header.present? }
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? 't-' or header.start_with? 's-' }
|
||||
end
|
||||
|
||||
|
|
@ -71,10 +72,28 @@ class SurveyResponsesDataLoader
|
|||
AcademicYear.find_by_date response_date(row)
|
||||
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_survey_items
|
||||
private_class_method :get_survey_item_ids_from_headers
|
||||
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
|
||||
|
||||
module StringMonkeyPatches
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ describe AdminDataLoader do
|
|||
describe 'output to console' 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", '')
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue