mirror of
https://github.com/edcommonwealth/Dashboard.git
synced 2026-03-07 21:38:14 -08:00
dirty commit: can't get references to work correctly between any tables
This commit is contained in:
parent
e1f0b78236
commit
a4fddbeced
183 changed files with 5461 additions and 5 deletions
29
app/presenters/dashboard/admin_data_presenter.rb
Normal file
29
app/presenters/dashboard/admin_data_presenter.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
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:)
|
||||
@admin_data_items = admin_data_items
|
||||
end
|
||||
|
||||
def title
|
||||
"School admin data"
|
||||
end
|
||||
|
||||
def id
|
||||
"admin-data-items-#{@measure_id}"
|
||||
end
|
||||
|
||||
def reason_for_insufficiency
|
||||
"limited availability"
|
||||
end
|
||||
|
||||
def descriptions_and_availability
|
||||
@admin_data_items.map do |admin_data_item|
|
||||
Summary.new(admin_data_item.admin_data_item_id, admin_data_item.description,
|
||||
admin_data_item.admin_data_values.where(school:, academic_year:).count > 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
85
app/presenters/dashboard/analyze/bar_presenter.rb
Normal file
85
app/presenters/dashboard/analyze/bar_presenter.rb
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
class BarPresenter
|
||||
include AnalyzeHelper
|
||||
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
|
||||
|
||||
MINIMUM_BAR_HEIGHT = 2
|
||||
|
||||
def initialize(measure:, academic_year:, score:, x_position:, color:)
|
||||
@score = score
|
||||
@x_position = x_position
|
||||
@academic_year = academic_year
|
||||
@measure = measure
|
||||
@measure_id = measure.measure_id
|
||||
@color = color
|
||||
end
|
||||
|
||||
def y_offset
|
||||
benchmark_height = analyze_zone_height * 2
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
benchmark_height - bar_height_percentage
|
||||
else
|
||||
benchmark_height
|
||||
end
|
||||
end
|
||||
|
||||
def bar_color
|
||||
"fill-#{zone.type}"
|
||||
end
|
||||
|
||||
def bar_height_percentage
|
||||
bar_height = send("#{zone.type}_bar_height_percentage") || 0
|
||||
enforce_minimum_height(bar_height:)
|
||||
end
|
||||
|
||||
def percentage
|
||||
low_benchmark = zone.low_benchmark
|
||||
(score.average - low_benchmark) / (zone.high_benchmark - low_benchmark)
|
||||
end
|
||||
|
||||
def zone
|
||||
zones = Zones.new(
|
||||
watch_low_benchmark: measure.watch_low_benchmark,
|
||||
growth_low_benchmark: measure.growth_low_benchmark,
|
||||
approval_low_benchmark: measure.approval_low_benchmark,
|
||||
ideal_low_benchmark: measure.ideal_low_benchmark
|
||||
)
|
||||
zones.zone_for_score(score.average)
|
||||
end
|
||||
|
||||
def average
|
||||
average = score.average || 0
|
||||
|
||||
average.round(6)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enforce_minimum_height(bar_height:)
|
||||
bar_height < MINIMUM_BAR_HEIGHT ? MINIMUM_BAR_HEIGHT : bar_height
|
||||
end
|
||||
|
||||
def ideal_bar_height_percentage
|
||||
(percentage * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def approval_bar_height_percentage
|
||||
(percentage * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def growth_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def watch_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def warning_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
end
|
||||
end
|
||||
20
app/presenters/dashboard/analyze/graph/all_data.rb
Normal file
20
app/presenters/dashboard/analyze/graph/all_data.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class AllData
|
||||
include Analyze::Graph::Column
|
||||
def to_s
|
||||
"All Data"
|
||||
end
|
||||
|
||||
def slug
|
||||
"all-data"
|
||||
end
|
||||
|
||||
def columns
|
||||
[AllStudent, AllTeacher, AllAdmin, GroupedBarColumnPresenter]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
39
app/presenters/dashboard/analyze/graph/column/all_admin.rb
Normal file
39
app/presenters/dashboard/analyze/graph/column/all_admin.rb
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
class AllAdmin < GroupedBarColumnPresenter
|
||||
def label
|
||||
%w[All Admin]
|
||||
end
|
||||
|
||||
def basis
|
||||
"admin data"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
!measure.includes_admin_data_items?
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
!academic_years.any? do |year|
|
||||
measure.sufficient_admin_data?(school:, academic_year: year)
|
||||
end
|
||||
end
|
||||
|
||||
def insufficiency_message
|
||||
["data not", "available"]
|
||||
end
|
||||
|
||||
def score(year_index)
|
||||
measure.admin_score(school:, academic_year: academic_years[year_index])
|
||||
end
|
||||
|
||||
def type
|
||||
:admin
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/all_student.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/all_student.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
class AllStudent < GroupedBarColumnPresenter
|
||||
def label
|
||||
%w[All Students]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
!measure.includes_student_survey_items?
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
scores = academic_years.map do |year|
|
||||
measure.score(school:, academic_year: year)
|
||||
end
|
||||
|
||||
scores.all? { |score| !score.meets_student_threshold? }
|
||||
end
|
||||
|
||||
def score(year_index)
|
||||
measure.student_score(school:, academic_year: academic_years[year_index])
|
||||
end
|
||||
|
||||
def type
|
||||
:student
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
class AllSurveyData < GroupedBarColumnPresenter
|
||||
def label
|
||||
%w[Survey Data]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
scores = academic_years.map do |year|
|
||||
combined_score(school:, academic_year: year)
|
||||
end
|
||||
|
||||
scores.all? { |score| !score.meets_student_threshold? && !score.meets_teacher_threshold? }
|
||||
end
|
||||
|
||||
def score(year_index)
|
||||
combined_score(school:, academic_year: academic_years[year_index])
|
||||
end
|
||||
|
||||
def type
|
||||
:all_survey_data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def combined_score(school:, academic_year:)
|
||||
teacher_score = measure.teacher_score(school:, academic_year:)
|
||||
student_score = measure.student_score(school:, academic_year:)
|
||||
|
||||
averages = []
|
||||
averages << student_score.average unless student_score.average.nil?
|
||||
averages << teacher_score.average unless teacher_score.average.nil?
|
||||
average = averages.average if averages.length > 0
|
||||
combined_score = Score.new(average:, meets_student_threshold: student_score.meets_student_threshold,
|
||||
meets_teacher_threshold: teacher_score.meets_teacher_threshold)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
42
app/presenters/dashboard/analyze/graph/column/all_teacher.rb
Normal file
42
app/presenters/dashboard/analyze/graph/column/all_teacher.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
class AllTeacher < GroupedBarColumnPresenter
|
||||
def label
|
||||
%w[All Teachers]
|
||||
end
|
||||
|
||||
def basis
|
||||
"teacher surveys"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
!measure.includes_teacher_survey_items?
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
scores = academic_years.map do |year|
|
||||
measure.score(school:, academic_year: year)
|
||||
end
|
||||
|
||||
scores.all? { |score| !score.meets_teacher_threshold? }
|
||||
end
|
||||
|
||||
def score(year_index)
|
||||
measure.teacher_score(school:, academic_year: academic_years[year_index])
|
||||
end
|
||||
|
||||
def type
|
||||
:teacher
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(survey_item: measure.teacher_survey_items, school:,
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module EllColumn
|
||||
class Ell < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::EllColumn::ScoreForEll
|
||||
include Analyze::Graph::Column::EllColumn::EllCount
|
||||
def label
|
||||
%w[ELL]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def ell
|
||||
::Ell.find_by_slug "ell"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module EllColumn
|
||||
module EllCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(ell:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module EllColumn
|
||||
class NotEll < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::EllColumn::ScoreForEll
|
||||
include Analyze::Graph::Column::EllColumn::EllCount
|
||||
def label
|
||||
["Not ELL"]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def ell
|
||||
::Ell.find_by_slug "not-ell"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module EllColumn
|
||||
module ScoreForEll
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_ell(measure.student_survey_items, school, academic_year,
|
||||
ell)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
ell:, survey_item: measure.student_survey_items).group(:ell).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module EllColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::EllColumn::ScoreForEll
|
||||
include Analyze::Graph::Column::EllColumn::EllCount
|
||||
def label
|
||||
%w[Unknown]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def ell
|
||||
::Ell.find_by_slug "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
class Female < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::GenderColumn::ScoreForGender
|
||||
include Analyze::Graph::Column::GenderColumn::GenderCount
|
||||
def label
|
||||
%w[Female]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def gender
|
||||
::Gender.find_by_qualtrics_code 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
module GenderCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(gender:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
class Male < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::GenderColumn::ScoreForGender
|
||||
include Analyze::Graph::Column::GenderColumn::GenderCount
|
||||
def label
|
||||
%w[Male]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def gender
|
||||
::Gender.find_by_qualtrics_code 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
class NonBinary < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::GenderColumn::ScoreForGender
|
||||
include Analyze::Graph::Column::GenderColumn::GenderCount
|
||||
def label
|
||||
%w[Non-Binary]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def gender
|
||||
::Gender.find_by_qualtrics_code 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
module ScoreForGender
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_gender(measure.student_survey_items, school, academic_year,
|
||||
gender)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
gender:, survey_item: measure.student_survey_items).group(:gender).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module GenderColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::GenderColumn::ScoreForGender
|
||||
include Analyze::Graph::Column::GenderColumn::GenderCount
|
||||
def label
|
||||
%w[Unknown]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def gender
|
||||
::Gender.find_by_qualtrics_code 99
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/eight.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/eight.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Eight < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 8]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
8
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Eleven < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 11]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
11
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/five.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/five.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Five < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 5]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/four.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/four.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Four < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 4]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
module GradeCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(grade:, survey_item: measure.student_survey_items, school:,
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/nine.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/nine.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Nine < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 9]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
9
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/one.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/one.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class One < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 1]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
module ScoreForGrade
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_grade(measure.student_survey_items, school,
|
||||
academic_year, grade)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
survey_item: measure.student_survey_items).group(:grade).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/seven.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/seven.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Seven < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 7]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
7
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/six.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/six.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Six < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 6]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
6
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/ten.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/ten.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Ten < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 10]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/three.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/three.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Three < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 3]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Twelve < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 12]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
12
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/two.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/two.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Two < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Grade 2]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
33
app/presenters/dashboard/analyze/graph/column/grade/zero.rb
Normal file
33
app/presenters/dashboard/analyze/graph/column/grade/zero.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module Grade
|
||||
class Zero < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::Grade::ScoreForGrade
|
||||
include Analyze::Graph::Column::Grade::GradeCount
|
||||
def label
|
||||
%w[Kindergarten]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def grade
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
class GroupedBarColumnPresenter
|
||||
include AnalyzeHelper
|
||||
|
||||
attr_reader :measure_name, :measure_id, :category, :position, :measure, :school, :academic_years,
|
||||
:number_of_columns
|
||||
|
||||
def initialize(measure:, school:, academic_years:, position:, number_of_columns:)
|
||||
@measure = measure
|
||||
@measure_name = @measure.name
|
||||
@measure_id = @measure.measure_id
|
||||
@category = @measure.subcategory.category
|
||||
@school = school
|
||||
@academic_years = academic_years
|
||||
@position = position
|
||||
@number_of_columns = number_of_columns
|
||||
end
|
||||
|
||||
def academic_year_for_year_index(year_index)
|
||||
academic_years[year_index]
|
||||
end
|
||||
|
||||
def score(year_index)
|
||||
measure.score(school:, academic_year: academic_years[year_index]) || 0
|
||||
end
|
||||
|
||||
def bars
|
||||
@bars ||= yearly_scores.map.each_with_index do |yearly_score, index|
|
||||
year = yearly_score.year
|
||||
Analyze::BarPresenter.new(measure:, academic_year: year,
|
||||
score: yearly_score.score,
|
||||
x_position: bar_x(index),
|
||||
color: bar_color(year))
|
||||
end
|
||||
end
|
||||
|
||||
def label
|
||||
%w[All Data]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
scores = academic_years.map do |year|
|
||||
measure.score(school:, academic_year: year)
|
||||
end
|
||||
|
||||
scores.all? do |score|
|
||||
!score.meets_teacher_threshold? && !score.meets_student_threshold? && !score.meets_admin_data_threshold?
|
||||
end
|
||||
end
|
||||
|
||||
def column_midpoint
|
||||
zone_label_width + (grouped_chart_column_width * (position + 1)) - (grouped_chart_column_width / 2)
|
||||
end
|
||||
|
||||
def bar_width
|
||||
min_bar_width(10.5 / data_sources)
|
||||
end
|
||||
|
||||
def min_bar_width(number)
|
||||
min_width = 2
|
||||
number < min_width ? min_width : number
|
||||
end
|
||||
|
||||
def message_x
|
||||
column_midpoint - message_width / 2
|
||||
end
|
||||
|
||||
def message_y
|
||||
17
|
||||
end
|
||||
|
||||
def message_width
|
||||
20
|
||||
end
|
||||
|
||||
def message_height
|
||||
34
|
||||
end
|
||||
|
||||
def column_end_x
|
||||
zone_label_width + (grouped_chart_column_width * (position + 1))
|
||||
end
|
||||
|
||||
def column_start_x
|
||||
zone_label_width + (grouped_chart_column_width * position)
|
||||
end
|
||||
|
||||
def grouped_chart_column_width
|
||||
graph_width / data_sources
|
||||
end
|
||||
|
||||
def bar_label_height
|
||||
(100 - ((100 - analyze_graph_height) / 2))
|
||||
end
|
||||
|
||||
def data_sources
|
||||
number_of_columns
|
||||
end
|
||||
|
||||
def basis
|
||||
"student surveys"
|
||||
end
|
||||
|
||||
def type
|
||||
:all_data
|
||||
end
|
||||
|
||||
def show_popover?
|
||||
%i[student teacher].include? type
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
|
||||
def popover_content(year_index)
|
||||
"#{n_size(year_index)} #{type.to_s.capitalize}s"
|
||||
end
|
||||
|
||||
def insufficiency_message
|
||||
["survey response", "rate below 25%"]
|
||||
end
|
||||
|
||||
def sufficient_data?(year_index)
|
||||
case basis
|
||||
when "student"
|
||||
score(year_index).meets_student_threshold
|
||||
when "teacher"
|
||||
score(year_index).meets_teacher_threshold
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def grades(year_index)
|
||||
Respondent.by_school_and_year(school:, academic_year: academic_years[year_index]).enrollment_by_grade.keys
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
YearlyScore = Struct.new(:year, :score)
|
||||
def yearly_scores
|
||||
yearly_scores = academic_years.each_with_index.map do |year, index|
|
||||
YearlyScore.new(year, score(index))
|
||||
end
|
||||
yearly_scores.reject do |yearly_score|
|
||||
yearly_score.score.blank?
|
||||
end
|
||||
end
|
||||
|
||||
def bar_color(year)
|
||||
@available_academic_years ||= AcademicYear.order(:range).all
|
||||
colors[@available_academic_years.find_index(year)]
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
class Disadvantaged < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::IncomeColumn::ScoreForIncome
|
||||
include Analyze::Graph::Column::IncomeColumn::IncomeCount
|
||||
def label
|
||||
%w[Economically Disadvantaged]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def income
|
||||
Income.find_by_designation "Economically Disadvantaged - Y"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
module IncomeCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(income:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
class NotDisadvantaged < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::IncomeColumn::ScoreForIncome
|
||||
include Analyze::Graph::Column::IncomeColumn::IncomeCount
|
||||
def label
|
||||
["Not Economically", "Disadvantaged"]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def income
|
||||
Income.find_by_designation "Economically Disadvantaged - N"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
module ScoreForIncome
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_income(measure.student_survey_items, school, academic_year,
|
||||
income)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
income:, survey_item: measure.student_survey_items).group(:income).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::IncomeColumn::ScoreForIncome
|
||||
include Analyze::Graph::Column::IncomeColumn::IncomeCount
|
||||
def label
|
||||
["Unknown"]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def income
|
||||
Income.find_by_designation "Unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class AmericanIndian < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[American Indian]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class Asian < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[Asian]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class Black < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[Black]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class Hispanic < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[Hispanic]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class MiddleEastern < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
|
||||
def label
|
||||
%w[Middle Eastern]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 8
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class Multiracial < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[Multiracial]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 100
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
module RaceCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where(
|
||||
school:, academic_year: academic_years[year_index],
|
||||
survey_item: measure.student_survey_items
|
||||
).where("student_races.race_id": race.id).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[Not Listed]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 99
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module RaceColumn
|
||||
class White < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::ScoreForRace
|
||||
include Analyze::Graph::Column::RaceColumn::RaceCount
|
||||
def label
|
||||
%w[White]
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def race
|
||||
Race.find_by_qualtrics_code 5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module ScoreForRace
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
survey_items = measure.student_survey_items
|
||||
|
||||
averages = SurveyItemResponse.averages_for_race(school, academic_year, race)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item.id]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
number_of_students_for_a_racial_group = SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where(
|
||||
school:, academic_year:
|
||||
).where("student_races.race_id": race.id).distinct.pluck(:student_id).count
|
||||
number_of_students_for_a_racial_group >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class NotSped < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
["Not Special", "Education"]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "not-special-education"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
module ScoreForSped
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_sped(measure.student_survey_items, school, academic_year,
|
||||
sped)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
sped:, survey_item: measure.student_survey_items).group(:sped).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class Sped < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
%w[Special Education]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "special-education"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
module SpedCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(sped:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
%w[Unknown]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsAndTeachers
|
||||
include Analyze::Graph::Column
|
||||
def to_s
|
||||
'Students & Teachers'
|
||||
end
|
||||
|
||||
def slug
|
||||
'students-and-teachers'
|
||||
end
|
||||
|
||||
def columns
|
||||
[AllStudent, AllTeacher, AllSurveyData]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
44
app/presenters/dashboard/analyze/graph/students_by_ell.rb
Normal file
44
app/presenters/dashboard/analyze/graph/students_by_ell.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByEll
|
||||
include Analyze::Graph::Column::EllColumn
|
||||
attr_reader :ells
|
||||
|
||||
def initialize(ells:)
|
||||
@ells = ells
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by Ell"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-ell"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
ells.each do |ell|
|
||||
array << column_for_ell_code(code: ell.slug)
|
||||
end
|
||||
array.sort_by!(&:to_s)
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_ell_code(code:)
|
||||
CFR[code]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"ell" => Analyze::Graph::Column::EllColumn::Ell,
|
||||
"not-ell" => Analyze::Graph::Column::EllColumn::NotEll,
|
||||
"unknown" => Analyze::Graph::Column::EllColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
45
app/presenters/dashboard/analyze/graph/students_by_gender.rb
Normal file
45
app/presenters/dashboard/analyze/graph/students_by_gender.rb
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByGender
|
||||
include Analyze::Graph::Column::GenderColumn
|
||||
attr_reader :genders
|
||||
|
||||
def initialize(genders:)
|
||||
@genders = genders
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by Gender"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-gender"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
genders.each do |gender|
|
||||
array << column_for_gender_code(code: gender.qualtrics_code)
|
||||
end
|
||||
array.sort_by!(&:to_s)
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_gender_code(code:)
|
||||
CFR[code]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
1 => Analyze::Graph::Column::GenderColumn::Female,
|
||||
2 => Analyze::Graph::Column::GenderColumn::Male,
|
||||
4 => Analyze::Graph::Column::GenderColumn::NonBinary,
|
||||
99 => Analyze::Graph::Column::GenderColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
53
app/presenters/dashboard/analyze/graph/students_by_grade.rb
Normal file
53
app/presenters/dashboard/analyze/graph/students_by_grade.rb
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByGrade
|
||||
include Analyze::Graph::Column::Grade
|
||||
attr_reader :grades
|
||||
|
||||
def initialize(grades:)
|
||||
@grades = grades
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by Grade"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-grade"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
grades.each do |grade|
|
||||
array << column_for_grade_code(code: grade)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_grade_code(code:)
|
||||
CFR[code]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
0 => Zero,
|
||||
1 => One,
|
||||
2 => Two,
|
||||
3 => Three,
|
||||
4 => Four,
|
||||
5 => Five,
|
||||
6 => Six,
|
||||
7 => Seven,
|
||||
8 => Eight,
|
||||
9 => Nine,
|
||||
10 => Ten,
|
||||
11 => Eleven,
|
||||
12 => Twelve
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
42
app/presenters/dashboard/analyze/graph/students_by_income.rb
Normal file
42
app/presenters/dashboard/analyze/graph/students_by_income.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByIncome
|
||||
attr_reader :incomes
|
||||
|
||||
def initialize(incomes:)
|
||||
@incomes = incomes
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by income"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-income"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
incomes.each do |income|
|
||||
array << column_for_income_code(code: income.slug)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_income_code(code:)
|
||||
CFR[code.to_s]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"economically-disadvantaged-y" => Analyze::Graph::Column::IncomeColumn::Disadvantaged,
|
||||
"economically-disadvantaged-n" => Analyze::Graph::Column::IncomeColumn::NotDisadvantaged,
|
||||
"unknown" => Analyze::Graph::Column::IncomeColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
47
app/presenters/dashboard/analyze/graph/students_by_race.rb
Normal file
47
app/presenters/dashboard/analyze/graph/students_by_race.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByRace
|
||||
attr_reader :races
|
||||
|
||||
def initialize(races:)
|
||||
@races = races
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by Race"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-race"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
races.each do |race|
|
||||
array << column_for_race_code(code: race.qualtrics_code)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_race_code(code:)
|
||||
CFR[code.to_s]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"1" => Analyze::Graph::Column::RaceColumn::AmericanIndian,
|
||||
"2" => Analyze::Graph::Column::RaceColumn::Asian,
|
||||
"3" => Analyze::Graph::Column::RaceColumn::Black,
|
||||
"4" => Analyze::Graph::Column::RaceColumn::Hispanic,
|
||||
"5" => Analyze::Graph::Column::RaceColumn::White,
|
||||
"8" => Analyze::Graph::Column::RaceColumn::MiddleEastern,
|
||||
"99" => Analyze::Graph::Column::RaceColumn::Unknown,
|
||||
"100" => Analyze::Graph::Column::RaceColumn::Multiracial
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
43
app/presenters/dashboard/analyze/graph/students_by_sped.rb
Normal file
43
app/presenters/dashboard/analyze/graph/students_by_sped.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsBySped
|
||||
include Analyze::Graph::Column::SpedColumn
|
||||
attr_reader :speds
|
||||
|
||||
def initialize(speds:)
|
||||
@speds = speds
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by SpEd"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-sped"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
speds.each do |sped|
|
||||
array << column_for_sped_code(code: sped.slug)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_sped_code(code:)
|
||||
CFR[code]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"special-education" => Analyze::Graph::Column::SpedColumn::Sped,
|
||||
"not-special-education" => Analyze::Graph::Column::SpedColumn::NotSped,
|
||||
"unknown" => Analyze::Graph::Column::SpedColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/ell.rb
Normal file
13
app/presenters/dashboard/analyze/group/ell.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Ell
|
||||
def name
|
||||
"ELL"
|
||||
end
|
||||
|
||||
def slug
|
||||
"ell"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/gender.rb
Normal file
13
app/presenters/dashboard/analyze/group/gender.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Gender
|
||||
def name
|
||||
'Gender'
|
||||
end
|
||||
|
||||
def slug
|
||||
'gender'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/grade.rb
Normal file
13
app/presenters/dashboard/analyze/group/grade.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Grade
|
||||
def name
|
||||
'Grade'
|
||||
end
|
||||
|
||||
def slug
|
||||
'grade'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/income.rb
Normal file
13
app/presenters/dashboard/analyze/group/income.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Income
|
||||
def name
|
||||
'Income'
|
||||
end
|
||||
|
||||
def slug
|
||||
'income'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/race.rb
Normal file
13
app/presenters/dashboard/analyze/group/race.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Race
|
||||
def name
|
||||
'Race'
|
||||
end
|
||||
|
||||
def slug
|
||||
'race'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/dashboard/analyze/group/sped.rb
Normal file
13
app/presenters/dashboard/analyze/group/sped.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Sped
|
||||
def name
|
||||
"Special Education"
|
||||
end
|
||||
|
||||
def slug
|
||||
"sped"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
200
app/presenters/dashboard/analyze/presenter.rb
Normal file
200
app/presenters/dashboard/analyze/presenter.rb
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
module Analyze
|
||||
class Presenter
|
||||
attr_reader :params, :school, :academic_year
|
||||
|
||||
def initialize(params:, school:, academic_year:)
|
||||
@params = params
|
||||
@school = school
|
||||
@academic_year = academic_year
|
||||
end
|
||||
|
||||
def category
|
||||
@category ||= Category.find_by_category_id(params[:category]) || Category.order(:category_id).first
|
||||
end
|
||||
|
||||
def categories
|
||||
@categories = Category.all.order(:category_id)
|
||||
end
|
||||
|
||||
def subcategory
|
||||
@subcategory ||= Subcategory.find_by_subcategory_id(params[:subcategory]) || subcategories.first
|
||||
end
|
||||
|
||||
def subcategories
|
||||
@subcategories = category.subcategories.order(:subcategory_id)
|
||||
end
|
||||
|
||||
def measures
|
||||
@measures = subcategory.measures.order(:measure_id).includes(%i[admin_data_items subcategory])
|
||||
end
|
||||
|
||||
def academic_years
|
||||
@academic_years = AcademicYear.order(:range).all
|
||||
end
|
||||
|
||||
def selected_academic_years
|
||||
@selected_academic_years ||= begin
|
||||
year_params = params[:academic_years]
|
||||
return [] unless year_params
|
||||
|
||||
year_params.split(",").map { |year| AcademicYear.find_by_range(year) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def races
|
||||
@races ||= Race.all.order(designation: :ASC)
|
||||
end
|
||||
|
||||
def selected_races
|
||||
@selected_races ||= begin
|
||||
race_params = params[:races]
|
||||
return races unless race_params
|
||||
|
||||
race_params.split(",").map { |race| Race.find_by_slug race }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def ells
|
||||
@ells ||= Ell.all.order(slug: :ASC)
|
||||
end
|
||||
|
||||
def selected_ells
|
||||
@selected_ells ||= begin
|
||||
ell_params = params[:ells]
|
||||
return ells unless ell_params
|
||||
|
||||
ell_params.split(",").map { |ell| Ell.find_by_slug ell }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def speds
|
||||
@speds ||= Sped.all.order(id: :ASC)
|
||||
end
|
||||
|
||||
def selected_speds
|
||||
@selected_speds ||= begin
|
||||
sped_params = params[:speds]
|
||||
return speds unless sped_params
|
||||
|
||||
sped_params.split(",").map { |sped| Sped.find_by_slug sped }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def graphs
|
||||
@graphs ||= [Analyze::Graph::AllData.new,
|
||||
Analyze::Graph::StudentsAndTeachers.new,
|
||||
Analyze::Graph::StudentsByRace.new(races: selected_races),
|
||||
Analyze::Graph::StudentsByGrade.new(grades: selected_grades),
|
||||
Analyze::Graph::StudentsByGender.new(genders: selected_genders),
|
||||
Analyze::Graph::StudentsByIncome.new(incomes: selected_incomes),
|
||||
Analyze::Graph::StudentsByEll.new(ells: selected_ells),
|
||||
Analyze::Graph::StudentsBySped.new(speds: selected_speds)]
|
||||
end
|
||||
|
||||
def graph
|
||||
@graph ||= graphs.reduce(graphs.first) do |acc, graph|
|
||||
graph.slug == params[:graph] ? graph : acc
|
||||
end
|
||||
end
|
||||
|
||||
def selected_grades
|
||||
@selected_grades ||= begin
|
||||
grade_params = params[:grades]
|
||||
return grades unless grade_params
|
||||
|
||||
grade_params.split(",").map(&:to_i)
|
||||
end
|
||||
end
|
||||
|
||||
def selected_genders
|
||||
@selected_genders ||= begin
|
||||
gender_params = params[:genders]
|
||||
return genders unless gender_params
|
||||
|
||||
gender_params.split(",").sort.map { |gender| Gender.find_by_designation(gender) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def genders
|
||||
@genders ||= Gender.all.order(designation: :ASC)
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups = [Analyze::Group::Ell.new, Analyze::Group::Gender.new, Analyze::Group::Grade.new, Analyze::Group::Income.new,
|
||||
Analyze::Group::Race.new, Analyze::Group::Sped.new]
|
||||
end
|
||||
|
||||
def group
|
||||
@group ||= groups.reduce(groups.first) do |acc, group|
|
||||
group.slug == params[:group] ? group : acc
|
||||
end
|
||||
end
|
||||
|
||||
def slice
|
||||
@slice ||= slices.reduce(slices.first) do |acc, slice|
|
||||
slice.slug == params[:slice] ? slice : acc
|
||||
end
|
||||
end
|
||||
|
||||
def slices
|
||||
source.slices
|
||||
end
|
||||
|
||||
def source
|
||||
@source ||= sources.reduce(sources.first) do |acc, source|
|
||||
source.slug == params[:source] ? source : acc
|
||||
end
|
||||
end
|
||||
|
||||
def sources
|
||||
all_data_slices = [Analyze::Slice::AllData.new]
|
||||
all_data_source = Analyze::Source::AllData.new(slices: all_data_slices)
|
||||
|
||||
students_and_teachers = Analyze::Slice::StudentsAndTeachers.new
|
||||
students_by_group = Analyze::Slice::StudentsByGroup.new(races:, grades:)
|
||||
survey_data_slices = [students_and_teachers, students_by_group]
|
||||
survey_data_source = Analyze::Source::SurveyData.new(slices: survey_data_slices)
|
||||
|
||||
@sources = [all_data_source, survey_data_source]
|
||||
end
|
||||
|
||||
def grades
|
||||
@grades ||= SurveyItemResponse.where(school:, academic_year:)
|
||||
.where.not(grade: nil)
|
||||
.group(:grade)
|
||||
.select(:response_id)
|
||||
.distinct(:response_id)
|
||||
.count.reject do |_key, value|
|
||||
value < 10
|
||||
end.keys
|
||||
end
|
||||
|
||||
def incomes
|
||||
@incomes ||= Income.all
|
||||
end
|
||||
|
||||
def selected_incomes
|
||||
@selected_incomes ||= begin
|
||||
income_params = params[:incomes]
|
||||
return incomes unless income_params
|
||||
|
||||
income_params.split(",").map { |income| Income.find_by_slug(income) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def cache_objects
|
||||
[subcategory,
|
||||
selected_academic_years,
|
||||
graph,
|
||||
selected_races,
|
||||
selected_grades,
|
||||
grades,
|
||||
selected_genders,
|
||||
genders,
|
||||
selected_ells,
|
||||
ells,
|
||||
selected_speds,
|
||||
speds]
|
||||
end
|
||||
end
|
||||
end
|
||||
17
app/presenters/dashboard/analyze/slice/all_data.rb
Normal file
17
app/presenters/dashboard/analyze/slice/all_data.rb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
module Analyze
|
||||
module Slice
|
||||
class AllData
|
||||
def to_s
|
||||
'All Data'
|
||||
end
|
||||
|
||||
def slug
|
||||
'all-data'
|
||||
end
|
||||
|
||||
def graphs
|
||||
[Analyze::Graph::AllData.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
module Analyze
|
||||
module Slice
|
||||
class StudentsAndTeachers
|
||||
def to_s
|
||||
'Students & Teachers'
|
||||
end
|
||||
|
||||
def slug
|
||||
'students-and-teachers'
|
||||
end
|
||||
|
||||
def graphs
|
||||
[Analyze::Graph::StudentsAndTeachers.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
24
app/presenters/dashboard/analyze/slice/students_by_group.rb
Normal file
24
app/presenters/dashboard/analyze/slice/students_by_group.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
module Analyze
|
||||
module Slice
|
||||
class StudentsByGroup
|
||||
attr_reader :races, :grades
|
||||
|
||||
def initialize(races:, grades:)
|
||||
@races = races
|
||||
@grades = grades
|
||||
end
|
||||
|
||||
def to_s
|
||||
'Students by Group'
|
||||
end
|
||||
|
||||
def slug
|
||||
'students-by-group'
|
||||
end
|
||||
|
||||
def graphs
|
||||
[Analyze::Graph::StudentsByRace.new(races:), Analyze::Graph::StudentsByGrade.new(grades:)]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/presenters/dashboard/analyze/source/all_data.rb
Normal file
21
app/presenters/dashboard/analyze/source/all_data.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
module Analyze
|
||||
module Source
|
||||
class AllData
|
||||
attr_reader :slices
|
||||
|
||||
include Analyze::Slice
|
||||
|
||||
def initialize(slices:)
|
||||
@slices = slices
|
||||
end
|
||||
|
||||
def to_s
|
||||
'All Data'
|
||||
end
|
||||
|
||||
def slug
|
||||
'all-data'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/presenters/dashboard/analyze/source/survey_data.rb
Normal file
21
app/presenters/dashboard/analyze/source/survey_data.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
module Analyze
|
||||
module Source
|
||||
class SurveyData
|
||||
attr_reader :slices
|
||||
|
||||
include Analyze::Slice
|
||||
|
||||
def initialize(slices:)
|
||||
@slices = slices
|
||||
end
|
||||
|
||||
def to_s
|
||||
'Survey Data Only'
|
||||
end
|
||||
|
||||
def slug
|
||||
'survey-data-only'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
46
app/presenters/dashboard/background_presenter.rb
Normal file
46
app/presenters/dashboard/background_presenter.rb
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
module Dashboard
|
||||
class BackgroundPresenter
|
||||
include AnalyzeHelper
|
||||
attr_reader :num_of_columns
|
||||
|
||||
def initialize(num_of_columns:)
|
||||
@num_of_columns = num_of_columns
|
||||
end
|
||||
|
||||
def zone_label_x
|
||||
2
|
||||
end
|
||||
|
||||
def benchmark_y
|
||||
(analyze_zone_height * 2) - (benchmark_height / 2.0)
|
||||
end
|
||||
|
||||
def benchmark_height
|
||||
1
|
||||
end
|
||||
|
||||
def grouped_chart_column_width
|
||||
graph_width / data_sources
|
||||
end
|
||||
|
||||
def column_end_x(position)
|
||||
zone_label_width + (grouped_chart_column_width * position)
|
||||
end
|
||||
|
||||
def column_start_x(position)
|
||||
column_end_x(position - 1)
|
||||
end
|
||||
|
||||
def bar_label_height
|
||||
(100 - ((100 - analyze_graph_height) / 2))
|
||||
end
|
||||
|
||||
def zone_label_y(position)
|
||||
8.5 * (position + position - 1)
|
||||
end
|
||||
|
||||
def data_sources
|
||||
num_of_columns
|
||||
end
|
||||
end
|
||||
end
|
||||
71
app/presenters/dashboard/category_presenter.rb
Normal file
71
app/presenters/dashboard/category_presenter.rb
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class CategoryPresenter
|
||||
def initialize(category:)
|
||||
@category = category
|
||||
end
|
||||
|
||||
def id
|
||||
@category.category_id
|
||||
end
|
||||
|
||||
def name
|
||||
@category.name
|
||||
end
|
||||
|
||||
def description
|
||||
@category.description
|
||||
end
|
||||
|
||||
def short_description
|
||||
@category.short_description
|
||||
end
|
||||
|
||||
def slug
|
||||
@category.slug
|
||||
end
|
||||
|
||||
def icon_class
|
||||
icon_suffix = classes[id.to_sym]
|
||||
"fas fa-#{icon_suffix}"
|
||||
end
|
||||
|
||||
def icon_color_class
|
||||
color_suffix = colors[id.to_sym]
|
||||
"color-#{color_suffix}"
|
||||
end
|
||||
|
||||
def subcategories(academic_year:, school:)
|
||||
@category.subcategories.includes([:measures]).sort_by(&:subcategory_id).map do |subcategory|
|
||||
SubcategoryPresenter.new(
|
||||
subcategory:,
|
||||
academic_year:,
|
||||
school:
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def to_model
|
||||
@category
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def colors
|
||||
{ '1': "blue",
|
||||
'2': "red",
|
||||
'3': "black",
|
||||
'4': "lime",
|
||||
'5': "teal" }
|
||||
end
|
||||
|
||||
def classes
|
||||
{ '1': "apple-alt",
|
||||
'2': "school",
|
||||
'3': "users-cog",
|
||||
'4': "graduation-cap",
|
||||
'5': "heart" }
|
||||
end
|
||||
end
|
||||
end
|
||||
22
app/presenters/dashboard/data_item_presenter.rb
Normal file
22
app/presenters/dashboard/data_item_presenter.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class DataItemPresenter
|
||||
attr_reader :measure_id, :has_sufficient_data, :school, :academic_year
|
||||
|
||||
def initialize(measure_id:, has_sufficient_data:, school:, academic_year:)
|
||||
@measure_id = measure_id
|
||||
@has_sufficient_data = has_sufficient_data
|
||||
@school = school
|
||||
@academic_year = academic_year
|
||||
end
|
||||
|
||||
def data_item_accordion_id
|
||||
"data-item-accordion-#{@measure_id}"
|
||||
end
|
||||
|
||||
def sufficient_data?
|
||||
@has_sufficient_data
|
||||
end
|
||||
end
|
||||
end
|
||||
70
app/presenters/dashboard/gauge_presenter.rb
Normal file
70
app/presenters/dashboard/gauge_presenter.rb
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class GaugePresenter
|
||||
def initialize(zones:, score:)
|
||||
@zones = zones
|
||||
@score = score&.round(2)
|
||||
end
|
||||
|
||||
def title
|
||||
zone.type.to_s.titleize
|
||||
end
|
||||
|
||||
def color_class
|
||||
"fill-#{zone.type}"
|
||||
end
|
||||
|
||||
def score_percentage
|
||||
percentage_for @score
|
||||
end
|
||||
|
||||
def key_benchmark_percentage
|
||||
percentage_for @zones.approval_zone.low_benchmark
|
||||
end
|
||||
|
||||
def boundary_percentage_for(zone)
|
||||
case zone
|
||||
when :watch_low
|
||||
watch_low_boundary
|
||||
when :growth_low
|
||||
growth_low_boundary
|
||||
when :ideal_low
|
||||
ideal_low_boundary
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :score
|
||||
|
||||
private
|
||||
|
||||
def watch_low_boundary
|
||||
percentage_for @zones.watch_zone.low_benchmark
|
||||
end
|
||||
|
||||
def growth_low_boundary
|
||||
percentage_for @zones.growth_zone.low_benchmark
|
||||
end
|
||||
|
||||
def approval_low_boundary
|
||||
percentage_for @zones.approval_zone.low_benchmark
|
||||
end
|
||||
|
||||
def ideal_low_boundary
|
||||
percentage_for @zones.ideal_zone.low_benchmark
|
||||
end
|
||||
|
||||
def zone
|
||||
@zones.zone_for_score(@score)
|
||||
end
|
||||
|
||||
def percentage_for(number)
|
||||
return nil if number.nil?
|
||||
|
||||
zones_minimum = @zones.warning_zone.low_benchmark
|
||||
zones_maximum = @zones.ideal_zone.high_benchmark
|
||||
|
||||
(number - zones_minimum) / (zones_maximum - zones_minimum)
|
||||
end
|
||||
end
|
||||
end
|
||||
78
app/presenters/dashboard/measure_presenter.rb
Normal file
78
app/presenters/dashboard/measure_presenter.rb
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class MeasurePresenter
|
||||
attr_reader :measure, :academic_year, :school, :id, :name, :description
|
||||
|
||||
def initialize(measure:, academic_year:, school:)
|
||||
@measure = measure
|
||||
@academic_year = academic_year
|
||||
@school = school
|
||||
@id = measure_id
|
||||
@name = measure.name
|
||||
@description = measure.description
|
||||
end
|
||||
|
||||
def gauge_presenter
|
||||
GaugePresenter.new(zones:, score: score_for_measure.average)
|
||||
end
|
||||
|
||||
def data_item_accordion_id
|
||||
"data-item-accordion-#{@measure.measure_id}"
|
||||
end
|
||||
|
||||
def data_item_presenters
|
||||
[].tap do |array|
|
||||
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,
|
||||
growth_low_benchmark: @measure.growth_low_benchmark,
|
||||
approval_low_benchmark: @measure.approval_low_benchmark,
|
||||
ideal_low_benchmark: @measure.ideal_low_benchmark
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
110
app/presenters/dashboard/response_rate_presenter.rb
Normal file
110
app/presenters/dashboard/response_rate_presenter.rb
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
module Dashboard
|
||||
class ResponseRatePresenter
|
||||
attr_reader :focus, :academic_year, :school, :survey_items
|
||||
|
||||
def initialize(focus:, academic_year:, school:)
|
||||
@focus = focus
|
||||
@academic_year = academic_year
|
||||
@school = school
|
||||
if focus == :student
|
||||
@survey_items = Measure.all.flat_map do |measure|
|
||||
measure.student_survey_items_with_sufficient_responses(school:, academic_year:)
|
||||
end
|
||||
end
|
||||
@survey_items = SurveyItem.teacher_survey_items if focus == :teacher
|
||||
end
|
||||
|
||||
def date
|
||||
SurveyItemResponse.where(survey_item: survey_items, school:,
|
||||
academic_year:).order(recorded_date: :DESC).first&.recorded_date
|
||||
end
|
||||
|
||||
def percentage
|
||||
return 0 if respondents_count.zero?
|
||||
|
||||
cap_at_100(actual_count.to_f / respondents_count.to_f * 100).round
|
||||
end
|
||||
|
||||
def color
|
||||
percentage > 75 ? "purple" : "gold"
|
||||
end
|
||||
|
||||
def date_message
|
||||
return "" if date.nil?
|
||||
|
||||
"Response rate as of #{date.to_date.strftime('%m/%d/%y')}"
|
||||
end
|
||||
|
||||
def hover_message
|
||||
return "" if date.nil?
|
||||
|
||||
"Percentages based on #{actual_count} out of #{respondents_count.round} #{focus}s completing at least 25% of the survey."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cap_at_100(value)
|
||||
value > 100 ? 100 : value
|
||||
end
|
||||
|
||||
def actual_count
|
||||
if focus == :teacher
|
||||
response_count_for_survey_items(survey_items:)
|
||||
else
|
||||
non_early_ed_items = survey_items - SurveyItem.early_education_survey_items
|
||||
non_early_ed_count = response_count_for_survey_items(survey_items: non_early_ed_items)
|
||||
|
||||
early_ed_items = survey_items & SurveyItem.early_education_survey_items
|
||||
early_ed_count = SurveyItemResponse.where(school:, academic_year:,
|
||||
survey_item: early_ed_items)
|
||||
.group(:survey_item)
|
||||
.select(:response_id)
|
||||
.distinct
|
||||
.count
|
||||
.reduce(0) do |largest, row|
|
||||
count = row[1]
|
||||
if count > largest
|
||||
count
|
||||
else
|
||||
largest
|
||||
end
|
||||
end
|
||||
|
||||
non_early_ed_count + early_ed_count
|
||||
end
|
||||
end
|
||||
|
||||
def response_count_for_survey_items(survey_items:)
|
||||
SurveyItemResponse.where(school:, academic_year:,
|
||||
survey_item: survey_items).select(:response_id).distinct.count || 0
|
||||
end
|
||||
|
||||
def respondents_count
|
||||
return 0 if respondents.nil?
|
||||
|
||||
count = enrollment if focus == :student
|
||||
count = respondents.total_teachers if focus == :teacher
|
||||
count
|
||||
end
|
||||
|
||||
def enrollment
|
||||
SurveyItemResponse.where(school:, academic_year:, grade: grades,
|
||||
survey_item: SurveyItem.student_survey_items)
|
||||
.select(:grade)
|
||||
.distinct
|
||||
.pluck(:grade)
|
||||
.reject(&:nil?)
|
||||
.map do |grade|
|
||||
respondents.enrollment_by_grade[grade]
|
||||
end.sum.to_f
|
||||
end
|
||||
|
||||
def respondents
|
||||
@respondents ||= Respondent.by_school_and_year(school:, academic_year:)
|
||||
end
|
||||
|
||||
def grades
|
||||
respondents.enrollment_by_grade.keys
|
||||
end
|
||||
end
|
||||
end
|
||||
35
app/presenters/dashboard/student_survey_presenter.rb
Normal file
35
app/presenters/dashboard/student_survey_presenter.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class StudentSurveyPresenter < DataItemPresenter
|
||||
attr_reader :survey_items
|
||||
|
||||
def initialize(measure_id:, survey_items:, has_sufficient_data:, school:, academic_year:)
|
||||
super(measure_id:, has_sufficient_data:, school:, academic_year:)
|
||||
@survey_items = survey_items
|
||||
end
|
||||
|
||||
def title
|
||||
"Student survey"
|
||||
end
|
||||
|
||||
def id
|
||||
"student-survey-items-#{@measure_id}"
|
||||
end
|
||||
|
||||
def reason_for_insufficiency
|
||||
"low response rate"
|
||||
end
|
||||
|
||||
def descriptions_and_availability
|
||||
survey_items.reject do |survey_item|
|
||||
question_id = survey_item.survey_item_id.split("-")[2]
|
||||
if question_id.present?
|
||||
question_id.starts_with?("es")
|
||||
else
|
||||
false
|
||||
end
|
||||
end.map(&:description)
|
||||
end
|
||||
end
|
||||
end
|
||||
38
app/presenters/dashboard/subcategory_card_presenter.rb
Normal file
38
app/presenters/dashboard/subcategory_card_presenter.rb
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class SubcategoryCardPresenter
|
||||
attr_reader :name, :subcategory, :category, :subcategory_id
|
||||
|
||||
def initialize(subcategory:, zones:, score:)
|
||||
@name = subcategory.name
|
||||
@subcategory = subcategory
|
||||
@category = subcategory.category
|
||||
@subcategory_id = subcategory.subcategory_id
|
||||
@zones = zones
|
||||
@score = score
|
||||
end
|
||||
|
||||
def harvey_ball_icon
|
||||
"#{zone.type}-harvey-ball"
|
||||
end
|
||||
|
||||
def color
|
||||
zone.type.to_s
|
||||
end
|
||||
|
||||
def insufficient_data?
|
||||
zone.type == :insufficient_data
|
||||
end
|
||||
|
||||
def to_model
|
||||
subcategory
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def zone
|
||||
@zones.zone_for_score(@score)
|
||||
end
|
||||
end
|
||||
end
|
||||
87
app/presenters/dashboard/subcategory_presenter.rb
Normal file
87
app/presenters/dashboard/subcategory_presenter.rb
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
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
|
||||
@id = @subcategory.subcategory_id
|
||||
@name = @subcategory.name
|
||||
@description = @subcategory.description
|
||||
end
|
||||
|
||||
def gauge_presenter
|
||||
GaugePresenter.new(zones:, score: average_score)
|
||||
end
|
||||
|
||||
def subcategory_card_presenter
|
||||
SubcategoryCardPresenter.new(subcategory: @subcategory, zones:, score: average_score)
|
||||
end
|
||||
|
||||
def average_score
|
||||
@average_score ||= @subcategory.score(school: @school, academic_year: @academic_year)
|
||||
end
|
||||
|
||||
def student_response_rate
|
||||
return "N / A" if Respondent.where(school: @school, academic_year: @academic_year).count.zero?
|
||||
|
||||
"#{@subcategory.response_rate(school: @school, academic_year: @academic_year).student_response_rate.round}%"
|
||||
end
|
||||
|
||||
def teacher_response_rate
|
||||
return "N / A" if Respondent.where(school: @school, academic_year: @academic_year).count.zero?
|
||||
|
||||
"#{@subcategory.response_rate(school: @school, academic_year: @academic_year).teacher_response_rate.round}%"
|
||||
end
|
||||
|
||||
def admin_collection_rate
|
||||
rate = [admin_data_values_count, admin_data_item_count]
|
||||
format_a_non_applicable_rate rate
|
||||
end
|
||||
|
||||
def measure_presenters
|
||||
@subcategory.measures.sort_by(&:measure_id).map do |measure|
|
||||
MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def admin_data_values_count
|
||||
@subcategory.measures.map do |measure|
|
||||
measure.scales.map do |scale|
|
||||
scale.admin_data_items.map do |admin_data_item|
|
||||
admin_data_item.admin_data_values.where(school: @school, academic_year: @academic_year).count
|
||||
end
|
||||
end
|
||||
end.flatten.sum
|
||||
end
|
||||
|
||||
def admin_data_item_count
|
||||
measures = @subcategory.measures
|
||||
return AdminDataItem.for_measures(measures).count if @school.is_hs
|
||||
|
||||
AdminDataItem.non_hs_items_for_measures(measures).count
|
||||
end
|
||||
|
||||
def format_a_non_applicable_rate(rate)
|
||||
rate == [0, 0] ? %w[N A] : rate
|
||||
end
|
||||
|
||||
def zones
|
||||
Zones.new(
|
||||
watch_low_benchmark: measures.map(&:watch_low_benchmark).average,
|
||||
growth_low_benchmark: measures.map(&:growth_low_benchmark).average,
|
||||
approval_low_benchmark: measures.map(&:approval_low_benchmark).average,
|
||||
ideal_low_benchmark: measures.map(&:ideal_low_benchmark).average
|
||||
)
|
||||
end
|
||||
|
||||
def measures
|
||||
@measures ||= @subcategory.measures.includes([:admin_data_items]).order(:measure_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
35
app/presenters/dashboard/teacher_survey_presenter.rb
Normal file
35
app/presenters/dashboard/teacher_survey_presenter.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class TeacherSurveyPresenter < DataItemPresenter
|
||||
attr_reader :survey_items
|
||||
|
||||
def initialize(measure_id:, survey_items:, has_sufficient_data:, school:, academic_year:)
|
||||
super(measure_id:, has_sufficient_data:, school:, academic_year:)
|
||||
@survey_items = survey_items
|
||||
end
|
||||
|
||||
def title
|
||||
"Teacher survey"
|
||||
end
|
||||
|
||||
def id
|
||||
"teacher-survey-items-#{@measure_id}"
|
||||
end
|
||||
|
||||
def reason_for_insufficiency
|
||||
"low response rate"
|
||||
end
|
||||
|
||||
def descriptions_and_availability
|
||||
if @measure_id == "1B-i"
|
||||
return [Summary.new("1B-i", "Items available upon request to ECP",
|
||||
true)]
|
||||
end
|
||||
|
||||
survey_items.map do |survey_item|
|
||||
Summary.new(survey_item.survey_item_id, survey_item.prompt, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
118
app/presenters/dashboard/variance_chart_row_presenter.rb
Normal file
118
app/presenters/dashboard/variance_chart_row_presenter.rb
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class VarianceChartRowPresenter
|
||||
include Comparable
|
||||
|
||||
attr_reader :score, :measure_name, :measure_id, :category
|
||||
|
||||
def initialize(measure:, score:)
|
||||
@measure = measure
|
||||
@score = score.average
|
||||
@meets_teacher_threshold = score.meets_teacher_threshold?
|
||||
@meets_student_threshold = score.meets_student_threshold?
|
||||
@measure_name = @measure.name
|
||||
@measure_id = @measure.measure_id
|
||||
@category = @measure.subcategory.category
|
||||
end
|
||||
|
||||
def sufficient_data?
|
||||
@score != nil
|
||||
end
|
||||
|
||||
def bar_color
|
||||
"fill-#{zone.type}"
|
||||
end
|
||||
|
||||
def bar_width
|
||||
"#{(bar_width_percentage * 100).round(2)}%"
|
||||
end
|
||||
|
||||
def x_offset
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
"60%"
|
||||
else
|
||||
"#{((0.6 - bar_width_percentage) * 100).abs.round(2)}%"
|
||||
end
|
||||
end
|
||||
|
||||
def order
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
bar_width_percentage
|
||||
when :warning, :watch, :growth
|
||||
-bar_width_percentage
|
||||
when :insufficient_data
|
||||
-100
|
||||
end
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
other.order <=> order
|
||||
end
|
||||
|
||||
def show_partial_data_indicator?
|
||||
partial_data_sources.present?
|
||||
end
|
||||
|
||||
def partial_data_sources
|
||||
[].tap do |sources|
|
||||
sources << "teacher survey results" if @measure.includes_teacher_survey_items? && !@meets_teacher_threshold
|
||||
sources << "student survey results" if @measure.includes_student_survey_items? && !@meets_student_threshold
|
||||
sources << "administrative data" if @measure.includes_admin_data_items?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
IDEAL_ZONE_WIDTH_PERCENTAGE = 0.2
|
||||
APPROVAL_ZONE_WIDTH_PERCENTAGE = 0.2
|
||||
GROWTH_ZONE_WIDTH_PERCENTAGE = 0.2
|
||||
WATCH_ZONE_WIDTH_PERCENTAGE = 0.2
|
||||
WARNING_ZONE_WIDTH_PERCENTAGE = 0.2
|
||||
|
||||
def bar_width_percentage
|
||||
send("#{zone.type}_bar_width_percentage")
|
||||
end
|
||||
|
||||
def ideal_bar_width_percentage
|
||||
percentage * IDEAL_ZONE_WIDTH_PERCENTAGE + APPROVAL_ZONE_WIDTH_PERCENTAGE
|
||||
end
|
||||
|
||||
def approval_bar_width_percentage
|
||||
percentage * APPROVAL_ZONE_WIDTH_PERCENTAGE
|
||||
end
|
||||
|
||||
def growth_bar_width_percentage
|
||||
(1 - percentage) * GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
end
|
||||
|
||||
def watch_bar_width_percentage
|
||||
(1 - percentage) * WATCH_ZONE_WIDTH_PERCENTAGE + GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
end
|
||||
|
||||
def warning_bar_width_percentage
|
||||
(1 - percentage) * WARNING_ZONE_WIDTH_PERCENTAGE + WATCH_ZONE_WIDTH_PERCENTAGE + GROWTH_ZONE_WIDTH_PERCENTAGE
|
||||
end
|
||||
|
||||
def insufficient_data_bar_width_percentage
|
||||
0
|
||||
end
|
||||
|
||||
def percentage
|
||||
low_benchmark = zone.low_benchmark
|
||||
(@score - low_benchmark) / (zone.high_benchmark - low_benchmark)
|
||||
end
|
||||
|
||||
def zone
|
||||
zones = Zones.new(
|
||||
watch_low_benchmark: @measure.watch_low_benchmark,
|
||||
growth_low_benchmark: @measure.growth_low_benchmark,
|
||||
approval_low_benchmark: @measure.approval_low_benchmark,
|
||||
ideal_low_benchmark: @measure.ideal_low_benchmark
|
||||
)
|
||||
zones.zone_for_score(@score)
|
||||
end
|
||||
end
|
||||
end
|
||||
49
app/presenters/dashboard/zones.rb
Normal file
49
app/presenters/dashboard/zones.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Dashboard
|
||||
class Zones
|
||||
def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:)
|
||||
@watch_low_benchmark = watch_low_benchmark
|
||||
@growth_low_benchmark = growth_low_benchmark
|
||||
@approval_low_benchmark = approval_low_benchmark
|
||||
@ideal_low_benchmark = ideal_low_benchmark
|
||||
@warning_low_benchmark = 1
|
||||
end
|
||||
|
||||
Zone = Struct.new(:low_benchmark, :high_benchmark, :type)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
def watch_zone
|
||||
Zone.new(@watch_low_benchmark, @growth_low_benchmark, :watch)
|
||||
end
|
||||
|
||||
def growth_zone
|
||||
Zone.new(@growth_low_benchmark, @approval_low_benchmark, :growth)
|
||||
end
|
||||
|
||||
def approval_zone
|
||||
Zone.new(@approval_low_benchmark, @ideal_low_benchmark, :approval)
|
||||
end
|
||||
|
||||
def ideal_zone
|
||||
Zone.new(@ideal_low_benchmark, 5.0, :ideal)
|
||||
end
|
||||
|
||||
def insufficient_data
|
||||
Zone.new(Float::MIN, Float::MAX, :insufficient_data)
|
||||
end
|
||||
|
||||
def zone_for_score(score)
|
||||
all_zones.find { |zone| Score.new(average: score).in_zone?(zone:) } || insufficient_data
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue