diff --git a/.reek.yml b/.reek.yml index c6477ff6..3162652a 100644 --- a/.reek.yml +++ b/.reek.yml @@ -2,3 +2,5 @@ detectors: InstanceVariableAssumption: enabled: false + IrresponsibleModule: + enabled: false diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb index 2893a566..5f979744 100644 --- a/app/controllers/analyze_controller.rb +++ b/app/controllers/analyze_controller.rb @@ -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 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7414ec42..b70d11d8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::Base before_action :set_google_analytics_id before_action :set_hotjar_id diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index ad84a669..bc2c5280 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CategoriesController < SqmApplicationController helper GaugeHelper def show diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 4e02ecb6..bb886b5f 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class HomeController < ApplicationController helper HeaderHelper def index diff --git a/app/controllers/overview_controller.rb b/app/controllers/overview_controller.rb index cfd716a1..0a185d09 100644 --- a/app/controllers/overview_controller.rb +++ b/app/controllers/overview_controller.rb @@ -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 diff --git a/app/controllers/sqm_application_controller.rb b/app/controllers/sqm_application_controller.rb index c0061866..160756cd 100644 --- a/app/controllers/sqm_application_controller.rb +++ b/app/controllers/sqm_application_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SqmApplicationController < ApplicationController protect_from_forgery with: :exception, prepend: true before_action :set_schools_and_districts diff --git a/app/helpers/analyze_helper.rb b/app/helpers/analyze_helper.rb index e74607ca..dada79a1 100644 --- a/app/helpers/analyze_helper.rb +++ b/app/helpers/analyze_helper.rb @@ -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 diff --git a/app/helpers/color_helper.rb b/app/helpers/color_helper.rb deleted file mode 100644 index 8acffe49..00000000 --- a/app/helpers/color_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ColorHelper -end diff --git a/app/helpers/header_helper.rb b/app/helpers/header_helper.rb index ce38639b..dcf5dfc5 100644 --- a/app/helpers/header_helper.rb +++ b/app/helpers/header_helper.rb @@ -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:) diff --git a/app/helpers/variance_helper.rb b/app/helpers/variance_helper.rb index 343652e0..d216559a 100644 --- a/app/helpers/variance_helper.rb +++ b/app/helpers/variance_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module VarianceHelper def heading_gutter 30 diff --git a/app/models/academic_year.rb b/app/models/academic_year.rb index 1f0224ae..f4f4464a 100644 --- a/app/models/academic_year.rb +++ b/app/models/academic_year.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AcademicYear < ActiveRecord::Base def self.find_by_date(date) if date.month > 6 diff --git a/app/models/admin_data_item.rb b/app/models/admin_data_item.rb index 43ea3a20..9108a4d2 100644 --- a/app/models/admin_data_item.rb +++ b/app/models/admin_data_item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AdminDataItem < ActiveRecord::Base belongs_to :scale has_many :admin_data_values diff --git a/app/models/admin_data_value.rb b/app/models/admin_data_value.rb index 241fe31e..9329b875 100644 --- a/app/models/admin_data_value.rb +++ b/app/models/admin_data_value.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AdminDataValue < ApplicationRecord belongs_to :school belongs_to :admin_data_item diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba8..71fbba5b 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end diff --git a/app/models/category.rb b/app/models/category.rb index 3b3d34b6..836975af 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Category < ActiveRecord::Base include FriendlyId friendly_id :name, use: [:slugged] diff --git a/app/models/data_availability.rb b/app/models/data_availability.rb index eb86493c..5f710a1d 100644 --- a/app/models/data_availability.rb +++ b/app/models/data_availability.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class DataAvailability < Struct.new(:id, :description, :available?) end diff --git a/app/models/district.rb b/app/models/district.rb index 49873b2d..df88a35a 100644 --- a/app/models/district.rb +++ b/app/models/district.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class District < ApplicationRecord has_many :schools diff --git a/app/models/legacy.rb b/app/models/legacy.rb index 4938c0f1..dfe3ee6f 100644 --- a/app/models/legacy.rb +++ b/app/models/legacy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Legacy def self.table_name_prefix 'legacy_' diff --git a/app/models/measure.rb b/app/models/measure.rb index fb3f9e09..e436926c 100644 --- a/app/models/measure.rb +++ b/app/models/measure.rb @@ -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 diff --git a/app/models/respondent.rb b/app/models/respondent.rb index fcd86dcb..48f508b7 100644 --- a/app/models/respondent.rb +++ b/app/models/respondent.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Respondent < ApplicationRecord belongs_to :school belongs_to :academic_year diff --git a/app/models/response_rate.rb b/app/models/response_rate.rb index 250f5e20..75862ab7 100644 --- a/app/models/response_rate.rb +++ b/app/models/response_rate.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ResponseRate < ApplicationRecord belongs_to :subcategory belongs_to :school diff --git a/app/models/response_rate_calculator.rb b/app/models/response_rate_calculator.rb index 41ca22fb..8941e9dd 100644 --- a/app/models/response_rate_calculator.rb +++ b/app/models/response_rate_calculator.rb @@ -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 diff --git a/app/models/scale.rb b/app/models/scale.rb index 6abb6f9d..1a106f70 100644 --- a/app/models/scale.rb +++ b/app/models/scale.rb @@ -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 diff --git a/app/models/school.rb b/app/models/school.rb index 10af82b4..add5e399 100644 --- a/app/models/school.rb +++ b/app/models/school.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class School < ApplicationRecord belongs_to :district diff --git a/app/models/score.rb b/app/models/score.rb index c53012e4..cb5521ad 100644 --- a/app/models/score.rb +++ b/app/models/score.rb @@ -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 diff --git a/app/models/student_response_rate_calculator.rb b/app/models/student_response_rate_calculator.rb index daab4f01..727d4226 100644 --- a/app/models/student_response_rate_calculator.rb +++ b/app/models/student_response_rate_calculator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StudentResponseRateCalculator include ResponseRateCalculator diff --git a/app/models/subcategory.rb b/app/models/subcategory.rb index 17250b96..2dd70c8d 100644 --- a/app/models/subcategory.rb +++ b/app/models/subcategory.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Subcategory < ActiveRecord::Base belongs_to :category, counter_cache: true diff --git a/app/models/survey.rb b/app/models/survey.rb index 068fa308..becf8df3 100644 --- a/app/models/survey.rb +++ b/app/models/survey.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Survey < ApplicationRecord belongs_to :academic_year belongs_to :school diff --git a/app/models/survey_item.rb b/app/models/survey_item.rb index 88ee2029..13e41690 100644 --- a/app/models/survey_item.rb +++ b/app/models/survey_item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SurveyItem < ActiveRecord::Base belongs_to :scale, counter_cache: true has_one :measure, through: :scale diff --git a/app/models/survey_item_response.rb b/app/models/survey_item_response.rb index f70dcfbf..737f4db3 100644 --- a/app/models/survey_item_response.rb +++ b/app/models/survey_item_response.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SurveyItemResponse < ActiveRecord::Base TEACHER_RESPONSE_THRESHOLD = 2 STUDENT_RESPONSE_THRESHOLD = 2 diff --git a/app/models/teacher_response_rate_calculator.rb b/app/models/teacher_response_rate_calculator.rb index 46e18508..f5c1a188 100644 --- a/app/models/teacher_response_rate_calculator.rb +++ b/app/models/teacher_response_rate_calculator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TeacherResponseRateCalculator include ResponseRateCalculator diff --git a/app/presenters/admin_data_presenter.rb b/app/presenters/admin_data_presenter.rb index 1f3cf130..ec896e6c 100644 --- a/app/presenters/admin_data_presenter.rb +++ b/app/presenters/admin_data_presenter.rb @@ -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:) diff --git a/app/presenters/analyze_bar_presenter.rb b/app/presenters/analyze_bar_presenter.rb index 0cb03204..6376a1f3 100644 --- a/app/presenters/analyze_bar_presenter.rb +++ b/app/presenters/analyze_bar_presenter.rb @@ -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 diff --git a/app/presenters/category_presenter.rb b/app/presenters/category_presenter.rb index a9daee48..eaaac965 100644 --- a/app/presenters/category_presenter.rb +++ b/app/presenters/category_presenter.rb @@ -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 diff --git a/app/presenters/data_item_presenter.rb b/app/presenters/data_item_presenter.rb index 605831a8..b66b17d2 100644 --- a/app/presenters/data_item_presenter.rb +++ b/app/presenters/data_item_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DataItemPresenter attr_reader :measure_id, :has_sufficient_data, :school, :academic_year diff --git a/app/presenters/gauge_presenter.rb b/app/presenters/gauge_presenter.rb index 5e81e683..00438954 100644 --- a/app/presenters/gauge_presenter.rb +++ b/app/presenters/gauge_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GaugePresenter def initialize(zones:, score:) @zones = zones diff --git a/app/presenters/grouped_bar_column_presenter.rb b/app/presenters/grouped_bar_column_presenter.rb index f4b5ed11..acce9da4 100644 --- a/app/presenters/grouped_bar_column_presenter.rb +++ b/app/presenters/grouped_bar_column_presenter.rb @@ -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 diff --git a/app/presenters/measure_presenter.rb b/app/presenters/measure_presenter.rb index 871e1708..24b9b32d 100644 --- a/app/presenters/measure_presenter.rb +++ b/app/presenters/measure_presenter.rb @@ -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, diff --git a/app/presenters/student_grouped_bar_column_presenter.rb b/app/presenters/student_grouped_bar_column_presenter.rb index 1d4f3a94..4377d6ed 100644 --- a/app/presenters/student_grouped_bar_column_presenter.rb +++ b/app/presenters/student_grouped_bar_column_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StudentGroupedBarColumnPresenter < GroupedBarColumnPresenter def label 'All Students' diff --git a/app/presenters/student_survey_presenter.rb b/app/presenters/student_survey_presenter.rb index f607b7d0..db6b524d 100644 --- a/app/presenters/student_survey_presenter.rb +++ b/app/presenters/student_survey_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StudentSurveyPresenter < DataItemPresenter attr_reader :survey_items diff --git a/app/presenters/subcategory_card_presenter.rb b/app/presenters/subcategory_card_presenter.rb index c252e330..92b0e0dc 100644 --- a/app/presenters/subcategory_card_presenter.rb +++ b/app/presenters/subcategory_card_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SubcategoryCardPresenter attr_reader :name, :subcategory, :category, :subcategory_id diff --git a/app/presenters/subcategory_presenter.rb b/app/presenters/subcategory_presenter.rb index 3426cdc4..5bfae595 100644 --- a/app/presenters/subcategory_presenter.rb +++ b/app/presenters/subcategory_presenter.rb @@ -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) diff --git a/app/presenters/teacher_grouped_bar_column_presenter.rb b/app/presenters/teacher_grouped_bar_column_presenter.rb index 85f1f40b..bd285452 100644 --- a/app/presenters/teacher_grouped_bar_column_presenter.rb +++ b/app/presenters/teacher_grouped_bar_column_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TeacherGroupedBarColumnPresenter < GroupedBarColumnPresenter def label 'All Teachers' diff --git a/app/presenters/teacher_survey_presenter.rb b/app/presenters/teacher_survey_presenter.rb index bce2d773..2c4da968 100644 --- a/app/presenters/teacher_survey_presenter.rb +++ b/app/presenters/teacher_survey_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TeacherSurveyPresenter < DataItemPresenter attr_reader :survey_items diff --git a/app/presenters/variance_chart_row_presenter.rb b/app/presenters/variance_chart_row_presenter.rb index 10a3b216..e49cab90 100644 --- a/app/presenters/variance_chart_row_presenter.rb +++ b/app/presenters/variance_chart_row_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class VarianceChartRowPresenter include Comparable diff --git a/app/presenters/zones.rb b/app/presenters/zones.rb index 0f4e115c..53cee49a 100644 --- a/app/presenters/zones.rb +++ b/app/presenters/zones.rb @@ -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 diff --git a/app/services/admin_data_loader.rb b/app/services/admin_data_loader.rb index 22424fc7..23635614 100644 --- a/app/services/admin_data_loader.rb +++ b/app/services/admin_data_loader.rb @@ -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 diff --git a/app/services/response_rate_loader.rb b/app/services/response_rate_loader.rb index 280a306b..e4d7e554 100644 --- a/app/services/response_rate_loader.rb +++ b/app/services/response_rate_loader.rb @@ -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' diff --git a/app/services/survey_responses_data_loader.rb b/app/services/survey_responses_data_loader.rb index 4639bc47..852a513a 100644 --- a/app/services/survey_responses_data_loader.rb +++ b/app/services/survey_responses_data_loader.rb @@ -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 diff --git a/spec/services/admin_data_loader_spec.rb b/spec/services/admin_data_loader_spec.rb index 0e01b8a4..09f10a26 100644 --- a/spec/services/admin_data_loader_spec.rb +++ b/spec/services/admin_data_loader_spec.rb @@ -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