mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
Add gender disagreggation
This commit is contained in:
parent
7f0faf8917
commit
7d7bee6498
18 changed files with 317 additions and 17 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
app/presenters/analyze/graph/column/gender/female.rb
Normal file
32
app/presenters/analyze/graph/column/gender/female.rb
Normal file
|
|
@ -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
|
||||
32
app/presenters/analyze/graph/column/gender/male.rb
Normal file
32
app/presenters/analyze/graph/column/gender/male.rb
Normal file
|
|
@ -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
|
||||
32
app/presenters/analyze/graph/column/gender/non_binary.rb
Normal file
32
app/presenters/analyze/graph/column/gender/non_binary.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
32
app/presenters/analyze/graph/column/gender/unknown.rb
Normal file
32
app/presenters/analyze/graph/column/gender/unknown.rb
Normal file
|
|
@ -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
|
||||
42
app/presenters/analyze/graph/students_by_gender.rb
Normal file
42
app/presenters/analyze/graph/students_by_gender.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@
|
|||
<% @genders.each do |gender| %>
|
||||
<div class="d-flex align-items-center">
|
||||
<input
|
||||
id="gender-<%= gender %>"
|
||||
id="gender-<%= gender.designation %>"
|
||||
class="m-3 gender-checkbox form-check-input"
|
||||
type="checkbox"
|
||||
name="gender-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%# <%= @selected_genders.include?(gender) ? "checked" : "" %1> %>
|
||||
<%= @selected_genders.include?(gender) ? "checked" : "" %>
|
||||
<%= @graph.slug == 'students-and-teachers' ? "disabled" : "" %>
|
||||
<%= @group.slug == 'gender' ? "" : "hidden" %>>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory} %>
|
||||
</div>
|
||||
|
||||
<% 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 %>
|
||||
<div class="bg-color-white flex-grow-1 col-9">
|
||||
<% @measures.each do |measure| %>
|
||||
<section class="mb-6">
|
||||
|
|
|
|||
|
|
@ -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,,
|
||||
|
|
|
|||
|
6
spec/fixtures/sample_demographics.csv
vendored
6
spec/fixtures/sample_demographics.csv
vendored
|
|
@ -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,,
|
||||
|
|
|
|||
|
45
spec/presenters/analyze/graph/column/gender/female_spec.rb
Normal file
45
spec/presenters/analyze/graph/column/gender/female_spec.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue