diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb index 9eaaec4e..e45c8748 100644 --- a/app/controllers/analyze_controller.rb +++ b/app/controllers/analyze_controller.rb @@ -3,7 +3,7 @@ class AnalyzeController < SqmApplicationController before_action :assign_categories, :assign_subcategories, :assign_measures, :assign_academic_years, :response_rate_timestamp, :races, :selected_races, :graph, :graphs, :background, :race_score_timestamp, - :sources, :group, :groups, :selected_grades, :grades, :slice, :genders, only: [:index] + :sources, :group, :groups, :selected_grades, :grades, :slice, :selected_genders, :genders, only: [:index] def index; end private @@ -76,7 +76,7 @@ class AnalyzeController < SqmApplicationController def graphs @graphs ||= [Analyze::Graph::StudentsAndTeachers.new, Analyze::Graph::StudentsByRace.new(races: selected_races), - Analyze::Graph::StudentsByGrade.new(grades: selected_grades)] + Analyze::Graph::StudentsByGrade.new(grades: selected_grades), Analyze::Graph::StudentsByGender.new(genders: selected_genders)] end def background @@ -148,6 +148,21 @@ class AnalyzeController < SqmApplicationController end.keys end + def selected_genders + @selected_genders ||= begin + gender_params = params[:genders] + return @selected_genders = genders unless gender_params + + gender_list = gender_params.split(',') if gender_params + if gender_list + gender_list = gender_list.map do |gender| + Gender.find_by_designation(gender) + end + end + gender_list + end + end + def genders @genders ||= Gender.all end diff --git a/app/javascript/controllers/analyze_controller.js b/app/javascript/controllers/analyze_controller.js index 9276a693..dad9712e 100644 --- a/app/javascript/controllers/analyze_controller.js +++ b/app/javascript/controllers/analyze_controller.js @@ -18,6 +18,8 @@ export default class extends Controller { this.selected_graph() + "&races=" + this.selected_races().join(",") + + "&genders=" + + this.selected_genders().join(",") + "&grades=" + this.selected_grades().join(","); @@ -79,10 +81,10 @@ export default class extends Controller { })[0]; if (selected_graph === 'students-and-teachers') { return selected_graph; - } - - if (this.selected_group() === 'race') { + } else if (this.selected_group() === 'race') { return 'students-by-race' + } else if (this.selected_group() === 'gender'){ + return 'students-by-gender' } else { return 'students-by-grade' } @@ -113,4 +115,17 @@ export default class extends Controller { return grades; } + + selected_genders() { + let gender_checkboxes = [...document.getElementsByName("gender-checkbox")] + let genders = gender_checkboxes + .filter((item) => { + return item.checked; + }) + .map((item) => { + return item.id.replace('gender-', ''); + }); + + return genders; + } } diff --git a/app/models/survey_item_response.rb b/app/models/survey_item_response.rb index 6c35f465..a11ee040 100644 --- a/app/models/survey_item_response.rb +++ b/app/models/survey_item_response.rb @@ -22,4 +22,8 @@ class SurveyItemResponse < ActiveRecord::Base academic_year: , grade:).group(:survey_item).average(:likert_score) } + scope :averages_for_gender, ->(survey_items, school, academic_year, gender) { + SurveyItemResponse.where(survey_item: survey_items, school:, + academic_year: , gender:).group(:survey_item).average(:likert_score) + } end diff --git a/app/presenters/analyze/graph/column/gender/female.rb b/app/presenters/analyze/graph/column/gender/female.rb new file mode 100644 index 00000000..fdcb0b25 --- /dev/null +++ b/app/presenters/analyze/graph/column/gender/female.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module Gender + class Female < GroupedBarColumnPresenter + include Analyze::Graph::Column::Gender::ScoreForGender + def label + '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 diff --git a/app/presenters/analyze/graph/column/gender/male.rb b/app/presenters/analyze/graph/column/gender/male.rb new file mode 100644 index 00000000..7a3eba06 --- /dev/null +++ b/app/presenters/analyze/graph/column/gender/male.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module Gender + class Male < GroupedBarColumnPresenter + include Analyze::Graph::Column::Gender::ScoreForGender + def label + '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 diff --git a/app/presenters/analyze/graph/column/gender/non_binary.rb b/app/presenters/analyze/graph/column/gender/non_binary.rb new file mode 100644 index 00000000..25caab34 --- /dev/null +++ b/app/presenters/analyze/graph/column/gender/non_binary.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module Gender + class NonBinary < GroupedBarColumnPresenter + include Analyze::Graph::Column::Gender::ScoreForGender + def label + '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 diff --git a/app/presenters/analyze/graph/column/gender/score_for_gender.rb b/app/presenters/analyze/graph/column/gender/score_for_gender.rb new file mode 100644 index 00000000..e904ce7c --- /dev/null +++ b/app/presenters/analyze/graph/column/gender/score_for_gender.rb @@ -0,0 +1,39 @@ +module Analyze + module Graph + module Column + module Gender + module ScoreForGender + def score(year_index) + academic_year = academic_years[year_index] + averages = SurveyItemResponse.averages_for_gender(measure.student_survey_items, school, academic_year, gender) + average = bubble_up_averages(averages:) + + scorify(average:, meets_student_threshold: sufficient_student_responses?(academic_year:)) + 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 scorify(average:, meets_student_threshold:) + return Score::NIL_SCORE unless meets_student_threshold + + Score.new(average:, + meets_teacher_threshold: false, + meets_student_threshold: true, + meets_admin_data_threshold: false) + end + + def sufficient_student_responses?(academic_year:) + yearly_counts = SurveyItemResponse.where(school: , academic_year: , gender:).group(:gender).select(:response_id).distinct(:response_id).count + yearly_counts.first[1] >= 10 + end + end + end + end + end +end diff --git a/app/presenters/analyze/graph/column/gender/unknown.rb b/app/presenters/analyze/graph/column/gender/unknown.rb new file mode 100644 index 00000000..b0adbf8c --- /dev/null +++ b/app/presenters/analyze/graph/column/gender/unknown.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module Gender + class Unknown < GroupedBarColumnPresenter + include Analyze::Graph::Column::Gender::ScoreForGender + def label + '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 diff --git a/app/presenters/analyze/graph/students_by_gender.rb b/app/presenters/analyze/graph/students_by_gender.rb new file mode 100644 index 00000000..aef0e794 --- /dev/null +++ b/app/presenters/analyze/graph/students_by_gender.rb @@ -0,0 +1,42 @@ +module Analyze + module Graph + class StudentsByGender + include Analyze::Graph::Column::Gender + 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 << Analyze::Graph::Column::AllStudent + end + end + + private + + def column_for_gender_code(code:) + CFR[code] + end + + CFR = { + 1 => Analyze::Graph::Column::Gender::Female, + 2 => Analyze::Graph::Column::Gender::Male, + 4 => Analyze::Graph::Column::Gender::NonBinary, + 99 => Analyze::Graph::Column::Gender::Unknown + }.freeze + end + end +end diff --git a/app/services/survey_responses_data_loader.rb b/app/services/survey_responses_data_loader.rb index 678f1d05..6704d6db 100644 --- a/app/services/survey_responses_data_loader.rb +++ b/app/services/survey_responses_data_loader.rb @@ -114,6 +114,7 @@ class Values def gender gender_code = row['gender'] || row['Gender'] || 99 gender_code = gender_code.to_i + gender_code = 4 if gender_code == 3 gender_code = 99 if gender_code.zero? Gender.find_by_qualtrics_code gender_code end diff --git a/app/views/analyze/_data_filters.html.erb b/app/views/analyze/_data_filters.html.erb index fb20e909..f1510033 100644 --- a/app/views/analyze/_data_filters.html.erb +++ b/app/views/analyze/_data_filters.html.erb @@ -64,13 +64,13 @@ <% @genders.each do |gender| %>
%> + <%= @selected_genders.include?(gender) ? "checked" : "" %> <%= @graph.slug == 'students-and-teachers' ? "disabled" : "" %> <%= @group.slug == 'gender' ? "" : "hidden" %>> diff --git a/app/views/analyze/index.html.erb b/app/views/analyze/index.html.erb index 29bfc320..3e481cd8 100644 --- a/app/views/analyze/index.html.erb +++ b/app/views/analyze/index.html.erb @@ -18,7 +18,7 @@ <%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory} %>
- <% cache [@subcategory, @school, @selected_academic_years, @response_rate_timestamp, @graph, @selected_races, @race_score_timestamp, @selected_grades, @grades] do %> + <% cache [@subcategory, @school, @selected_academic_years, @response_rate_timestamp, @graph, @selected_races, @race_score_timestamp, @selected_grades, @grades, @selected_genders, @genders] do %>
<% @measures.each do |measure| %>
diff --git a/data/demographics.csv b/data/demographics.csv index e71dac84..8ac4363f 100644 --- a/data/demographics.csv +++ b/data/demographics.csv @@ -1,9 +1,9 @@ Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender 1,American Indian or Alaskan Native,2,Male 2,Asian or Pacific Islander,1,Female -3,Black or African American,3,Another gender or gender identity not listed above -4,Hispanic or Latinx,4,Non-Binary -5,White or Caucasian,99,Unknown +3,Black or African American,4,Non-Binary +4,Hispanic or Latinx,99,Unknown +5,White or Caucasian,, 6,Prefer not to disclose,, 7,Prefer to self-describe,, 8,Middle Eastern,, diff --git a/spec/fixtures/sample_demographics.csv b/spec/fixtures/sample_demographics.csv index e71dac84..8ac4363f 100644 --- a/spec/fixtures/sample_demographics.csv +++ b/spec/fixtures/sample_demographics.csv @@ -1,9 +1,9 @@ Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender 1,American Indian or Alaskan Native,2,Male 2,Asian or Pacific Islander,1,Female -3,Black or African American,3,Another gender or gender identity not listed above -4,Hispanic or Latinx,4,Non-Binary -5,White or Caucasian,99,Unknown +3,Black or African American,4,Non-Binary +4,Hispanic or Latinx,99,Unknown +5,White or Caucasian,, 6,Prefer not to disclose,, 7,Prefer to self-describe,, 8,Middle Eastern,, diff --git a/spec/presenters/analyze/graph/column/gender/female_spec.rb b/spec/presenters/analyze/graph/column/gender/female_spec.rb new file mode 100644 index 00000000..ed05c6de --- /dev/null +++ b/spec/presenters/analyze/graph/column/gender/female_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' +include Analyze::Graph +include Analyze::Graph::Column::Gender +describe StudentsByRace do + let(:female) { create(:gender, qualtrics_code: 1, designation: 'Female') } + let(:school) { create(:school) } + let(:academic_year) { create(:academic_year, range: '1900-01') } + let(:academic_years) { [academic_year] } + let(:year_index) { academic_years.find_index(academic_year) } + + let(:measure_with_student_survey_items) { create(:measure, name: 'Student measure') } + let(:scale_with_student_survey_item) { create(:student_scale, measure: measure_with_student_survey_items) } + let(:student_survey_item) do + create(:student_survey_item, scale: scale_with_student_survey_item) + end + + before do + create_list(:survey_item_response, 1, survey_item: student_survey_item, school:, academic_year:, gender: female) + end + + context '.gender' do + it 'returns female gender' do + expect(Female.new(measure: measure_with_student_survey_items, school:, academic_years:, position: year_index, + number_of_columns: 1).gender).to eq female + end + + it 'returns the count of survey items for females for that school and academic_year ' do + female_column = Female.new(measure: measure_with_student_survey_items, school:, academic_years:, position: year_index, + number_of_columns: 1) + expect(female_column.sufficient_student_responses?(academic_year:)).to eq false + end + + context 'when there are more than 10 students who responded' do + before do + create_list(:survey_item_response, 10, survey_item: student_survey_item, school:, academic_year:, + gender: female) + end + it 'returns the count of survey items for females for that school and academic_year ' do + female_column = Female.new(measure: measure_with_student_survey_items, school:, academic_years:, position: year_index, + number_of_columns: 1) + expect(female_column.sufficient_student_responses?(academic_year:)).to eq true + end + end + end +end diff --git a/spec/services/demographic_loader_spec.rb b/spec/services/demographic_loader_spec.rb index a816041e..b05d8aa0 100644 --- a/spec/services/demographic_loader_spec.rb +++ b/spec/services/demographic_loader_spec.rb @@ -9,7 +9,7 @@ describe DemographicLoader do let(:gender_codes) do { - 'Female' => 1, 'Male' => 2, 'Another gender or gender identity not listed above' => 3, 'Non-Binary' => 4, 'Unknown' => 99 + 'Female' => 1, 'Male' => 2, 'Non-Binary' => 4, 'Unknown' => 99 } end @@ -37,7 +37,7 @@ describe DemographicLoader do end it 'loads all gender designations' do - expect(Gender.all.count).to eq 5 + expect(Gender.all.count).to eq 4 gender_codes.each do |key, value| expect(Gender.find_by_qualtrics_code(value)).not_to eq nil diff --git a/spec/services/survey_responses_data_loader_spec.rb b/spec/services/survey_responses_data_loader_spec.rb index ffcee629..8db45f23 100644 --- a/spec/services/survey_responses_data_loader_spec.rb +++ b/spec/services/survey_responses_data_loader_spec.rb @@ -187,7 +187,7 @@ end def assigns_gender_to_responses results = { 'student_survey_response_1' => female, 'student_survey_response_3' => male, - 'student_survey_response_4' => another_gender, + 'student_survey_response_4' => non_binary, 'student_survey_response_5' => non_binary, 'student_survey_response_6' => unknown_gender, 'student_survey_response_7' => unknown_gender } diff --git a/spec/views/analyze/index.html.erb_spec.rb b/spec/views/analyze/index.html.erb_spec.rb index 5c45d8b9..fdb3b95c 100644 --- a/spec/views/analyze/index.html.erb_spec.rb +++ b/spec/views/analyze/index.html.erb_spec.rb @@ -80,6 +80,15 @@ describe 'analyze/index' do (1..12).to_a end + let(:genders) do + DemographicLoader.load_data(filepath: 'spec/fixtures/sample_demographics.csv') + Gender.all + end + + let(:selected_genders) do + genders + end + let(:selected_grades) do grades end @@ -106,6 +115,8 @@ describe 'analyze/index' do assign :slice, slice assign :grades, grades assign :selected_grades, selected_grades + assign :genders, genders + assign :selected_genders, selected_genders create(:respondent, school:, academic_year:) create(:survey, school:, academic_year:) end