mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 13:38:18 -08:00
Create ui for data filters. Add listeners to direct to the correct url. Update ui based on the list of selected params
This commit is contained in:
parent
4f0b92fa79
commit
765ad6a624
35 changed files with 14776 additions and 2878 deletions
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -10,5 +10,3 @@ Metrics/ClassLength:
|
|||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class AnalyzeController < SqmApplicationController
|
||||
before_action :assign_categories, :assign_subcategories, :assign_measures, :assign_academic_years,
|
||||
:response_rate_timestamp, only: [:index]
|
||||
:response_rate_timestamp, :races, :selected_races, :graph, :graphs, only: [:index]
|
||||
def index; end
|
||||
|
||||
private
|
||||
|
|
@ -45,4 +45,31 @@ class AnalyzeController < SqmApplicationController
|
|||
end
|
||||
@response_rate_timestamp
|
||||
end
|
||||
|
||||
def races
|
||||
@races ||= Race.all.order(designation: :ASC)
|
||||
end
|
||||
|
||||
def selected_races
|
||||
@selected_races ||= begin
|
||||
race_params = params[:races]
|
||||
return @selected_races = races unless race_params
|
||||
|
||||
race_list = race_params.split(',') if race_params
|
||||
if race_list
|
||||
race_list = race_list.map do |race|
|
||||
Race.find_by_slug race
|
||||
end
|
||||
end
|
||||
race_list
|
||||
end
|
||||
end
|
||||
|
||||
def graph
|
||||
@graph ||= params[:graph] || 'students-and-teachers'
|
||||
end
|
||||
|
||||
def graphs
|
||||
@graphs ||= [AnalysisGraph::StudentsAndTeachers.new, AnalysisGraph::StudentsByGroup.new]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -84,4 +84,9 @@ module AnalyzeHelper
|
|||
|
||||
@empty_dataset[[school, academic_year]]
|
||||
end
|
||||
|
||||
def base_url
|
||||
analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category,
|
||||
subcategory: @subcategory)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
6
app/javascript/controllers/.eslintrc.bak
Normal file
6
app/javascript/controllers/.eslintrc.bak
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2020
|
||||
}
|
||||
}
|
||||
6
app/javascript/controllers/.eslintrc.json
Normal file
6
app/javascript/controllers/.eslintrc.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2020
|
||||
}
|
||||
}
|
||||
|
|
@ -2,18 +2,63 @@ import { Controller } from "@hotwired/stimulus";
|
|||
|
||||
// Connects to data-controller="analyze"
|
||||
export default class extends Controller {
|
||||
connect() {}
|
||||
connect() { }
|
||||
refresh(event) {
|
||||
let location = event.target.value;
|
||||
let year_checkboxes = [...document.getElementsByName("year-checkbox")];
|
||||
let base_url = event.target.value;
|
||||
|
||||
let selected_years = year_checkboxes
|
||||
let url =
|
||||
base_url +
|
||||
"&academic_years=" +
|
||||
this.selected_years().join(",") +
|
||||
"&graph=" +
|
||||
this.selected_graph() +
|
||||
"&races=" +
|
||||
this.selected_races().join(",");
|
||||
|
||||
this.go_to(url);
|
||||
}
|
||||
|
||||
|
||||
go_to(location) {
|
||||
window.location = location;
|
||||
}
|
||||
|
||||
selected_years() {
|
||||
let year_checkboxes = [...document.getElementsByName("year-checkbox")];
|
||||
let years = year_checkboxes
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
window.location = location + "&academic_years=" + selected_years.join(",");
|
||||
|
||||
return years;
|
||||
}
|
||||
|
||||
selected_graph() {
|
||||
let graphs = [...document.getElementsByName("graph")];
|
||||
let selected_graph = graphs
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
|
||||
return selected_graph[0];
|
||||
}
|
||||
|
||||
selected_races() {
|
||||
let race_checkboxes = [...document.getElementsByName("race-checkbox")]
|
||||
let races = race_checkboxes
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
|
||||
return races;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ class Seeder
|
|||
end
|
||||
end
|
||||
|
||||
def seed_demographics(csv_file)
|
||||
DemographicLoader.load_data(filepath: csv_file)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def marked?(mark)
|
||||
|
|
|
|||
|
|
@ -2,18 +2,23 @@
|
|||
|
||||
class AcademicYear < ActiveRecord::Base
|
||||
def self.find_by_date(date)
|
||||
if date.month > 6
|
||||
ay_range_start = date.year
|
||||
ay_range_end = date.year + 1
|
||||
else
|
||||
ay_range_start = date.year - 1
|
||||
ay_range_end = date.year
|
||||
end
|
||||
AcademicYear.find_by_range("#{ay_range_start}-#{ay_range_end.to_s[2, 3]}")
|
||||
year = parse_year_range(date:)
|
||||
AcademicYear.find_by_range("#{year.start}-#{year.end.to_s[2, 3]}")
|
||||
end
|
||||
|
||||
def formatted_range
|
||||
years = range.split('-')
|
||||
"#{years.first} – 20#{years.second}"
|
||||
end
|
||||
|
||||
def self.parse_year_range(date:)
|
||||
year = date.year
|
||||
if date.month > 6
|
||||
AcademicYearRange.new(year, year + 1)
|
||||
else
|
||||
AcademicYearRange.new(year - 1, year)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
AcademicYearRange = Struct.new(:start, :end)
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ class AdminDataValue < ApplicationRecord
|
|||
belongs_to :school
|
||||
belongs_to :admin_data_item
|
||||
belongs_to :academic_year
|
||||
validates :likert_score, numericality: { greater_than: 0, less_than_or_equal_to: 5 }
|
||||
end
|
||||
|
|
|
|||
11
app/models/analysis_graph/students_and_teachers.rb
Normal file
11
app/models/analysis_graph/students_and_teachers.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module AnalysisGraph
|
||||
class StudentsAndTeachers
|
||||
def to_s
|
||||
'Students & Teachers'
|
||||
end
|
||||
|
||||
def value
|
||||
'students-and-teachers'
|
||||
end
|
||||
end
|
||||
end
|
||||
11
app/models/analysis_graph/students_by_group.rb
Normal file
11
app/models/analysis_graph/students_by_group.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module AnalysisGraph
|
||||
class StudentsByGroup
|
||||
def to_s
|
||||
'Students by Group'
|
||||
end
|
||||
|
||||
def value
|
||||
'students-by-group'
|
||||
end
|
||||
end
|
||||
end
|
||||
4
app/models/race.rb
Normal file
4
app/models/race.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class Race < ApplicationRecord
|
||||
include FriendlyId
|
||||
friendly_id :designation, use: [:slugged]
|
||||
end
|
||||
31
app/services/demographic_loader.rb
Normal file
31
app/services/demographic_loader.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
class DemographicLoader
|
||||
def self.load_data(filepath:)
|
||||
CSV.parse(File.read(filepath), headers: true) do |row|
|
||||
qualtrics_code = row['Race Qualtrics Code'].to_i
|
||||
designation = row['Race/Ethnicity']
|
||||
next unless qualtrics_code && designation
|
||||
|
||||
if qualtrics_code.between?(6, 7)
|
||||
UnknownRace.new(qualtrics_code:, designation:)
|
||||
else
|
||||
KnownRace.new(qualtrics_code:, designation:)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class KnownRace
|
||||
def initialize(qualtrics_code:, designation:)
|
||||
Race.find_or_create_by!(qualtrics_code:, designation:)
|
||||
end
|
||||
end
|
||||
|
||||
class UnknownRace
|
||||
def initialize(qualtrics_code:, designation:)
|
||||
Race.find_or_create_by!(qualtrics_code: 99, designation: 'Unknown')
|
||||
end
|
||||
end
|
||||
28
app/views/analyze/_data_filters.html.erb
Normal file
28
app/views/analyze/_data_filters.html.erb
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<h3 class="sub-header-4 mt-5">Data Filters</h3>
|
||||
<div class="bg-gray p-3" data-controller="analyze">
|
||||
<% @graphs.each do |graph| %>
|
||||
<div>
|
||||
<input type="radio" id="<%= graph.value %>" name="graph"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= graph.value == @graph ? "checked" : "" %>>
|
||||
<label for="<%= graph.value %>"><%= graph.to_s %></label>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p class="sub-header-5 mt-3 font-size-14"> Select a group </p>
|
||||
|
||||
<% @races.each do |race | %>
|
||||
<div class="d-flex <%= race.slug %>">
|
||||
<input
|
||||
id="<%= race.slug %>"
|
||||
class="m-3 race-checkbox"
|
||||
type="checkbox"
|
||||
name="race-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= @selected_races.map(&:slug).include?(race.slug) ? "checked" : "" %>>
|
||||
<label for="<%= race.qualtrics_code %>"><%= race.designation %></label>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
14
app/views/analyze/_focus_area.html.erb
Normal file
14
app/views/analyze/_focus_area.html.erb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<h3 class="sub-header-4">Focus Area</h3>
|
||||
<p>Select a category & subcategory to analyze measure-level results</p>
|
||||
<select id="select-category" class="mx-3 form-select" data-id="category-dropdown" data-action="analyze#refresh">
|
||||
<% categories.each do |category| %>
|
||||
<option value="<%= analyze_category_link(district: district, school: school, academic_year: academic_year, category: category) %>" <%= category.id == category.id ? "selected": "" %>><%= "#{category.category_id}: #{category.name}" %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<select id="select-subcategory" class="mx-3 form-select mt-3" data-id="subcategory-dropdown" data-action="analyze#refresh">
|
||||
<% subcategories.each do |subcategory| %>
|
||||
<option value="<%= analyze_subcategory_link(district: district, school: school, academic_year: academic_year, category: category, subcategory: subcategory) %>" <%= subcategory.subcategory_id == subcategory.subcategory_id ? "selected": "" %>>
|
||||
<%= "#{subcategory.subcategory_id}: #{subcategory.name}" %>
|
||||
</option>
|
||||
<% end %>
|
||||
</select>
|
||||
32
app/views/analyze/_graph_background.html.erb
Normal file
32
app/views/analyze/_graph_background.html.erb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<g id="graph-background">
|
||||
<rect x="0" y="0" width="100%" height="<%= analyze_zone_height * 2 %>%" fill="#edecf0" />
|
||||
<rect x="0" y="<%= analyze_zone_height * 2 %>%" width="100%" height="<%= analyze_zone_height * 3 %>%" fill="#fffaee" />
|
||||
<rect x="0" y="0" width="100%" height="<%= analyze_graph_height %>%" fill="none" stroke="grey" />
|
||||
<line x1="<%= column_end_x(1) %>%" y1="0" x2="<%= column_end_x(1) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2" />
|
||||
<line x1="<%= column_end_x(2) %>%" y1="0" x2="<%= column_end_x(2) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2" />
|
||||
<rect x="0" y="<%= benchmark_y %>%" width="100%" height="<%= benchmark_height %>%" fill="black" />
|
||||
|
||||
<g id="zone-dividers" stroke-width="1">
|
||||
<line x1="0" y1="17%" x2="100%" y2="17%" stroke="white" />
|
||||
<line x1="0" y1="51%" x2="100%" y2="51%" stroke="#edecf0" />
|
||||
<line x1="0" y1="68%" x2="100%" y2="68%" stroke="#edecf0" />
|
||||
</g>
|
||||
|
||||
<g id="zone-labels">
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(1) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Ideal
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(2) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Approval
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(3) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Growth
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(4) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Watch
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(5) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Warning
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
|
|
@ -1,37 +1,7 @@
|
|||
<svg width="100%" height="<%= svg_height %>" >
|
||||
<g id="graph-background">
|
||||
<rect x="0" y="0" width="100%" height="<%= analyze_zone_height * 2 %>%" fill="#edecf0"/>
|
||||
<rect x="0" y="<%= analyze_zone_height * 2 %>%" width="100%" height="<%= analyze_zone_height * 3 %>%" fill="#fffaee"/>
|
||||
<rect x="0" y="0" width="100%" height="<%= analyze_graph_height %>%" fill="none" stroke="grey"/>
|
||||
<line x1="<%= column_end_x(1) %>%" y1="0" x2="<%= column_end_x(1) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
|
||||
<line x1="<%= column_end_x(2) %>%" y1="0" x2="<%= column_end_x(2) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
|
||||
<rect x="0" y="<%= benchmark_y %>%" width="100%" height="<%= benchmark_height %>%" fill="black"/>
|
||||
<g id="zone-dividers" stroke-width="1" >
|
||||
<line x1="0" y1="17%" x2="100%" y2="17%" stroke="white" />
|
||||
<line x1="0" y1="51%" x2="100%" y2="51%" stroke="#edecf0" />
|
||||
<line x1="0" y1="68%" x2="100%" y2="68%" stroke="#edecf0" />
|
||||
</g>
|
||||
<svg width="100%" height="<%= svg_height %>">
|
||||
<%= render "graph_background" %>
|
||||
|
||||
<g id="zone-labels">
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(1) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Ideal
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(2) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Approval
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(3) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Growth
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(4) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Watch
|
||||
</text>
|
||||
<text class="zone-header" x="<%= zone_label_x %>%" y="<%= zone_label_y(5) %>%" text-anchor="start" dominant-baseline="middle">
|
||||
Warning
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<% presenters = [StudentGroupedBarColumnPresenter, TeacherGroupedBarColumnPresenter, GroupedBarColumnPresenter] %>
|
||||
<% presenters = [StudentGroupedBarColumnPresenter, TeacherGroupedBarColumnPresenter, GroupedBarColumnPresenter] %>
|
||||
<% presenters.each_with_index do |presenter, index| %>
|
||||
<% p = presenter.new(measure: measure, school: @school, academic_years: @selected_academic_years, position: index ) %>
|
||||
<%= render partial: "grouped_bar_column", locals: {presenter: p} %>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 472 B |
|
|
@ -1,34 +1,33 @@
|
|||
<g class="grouped-bar-column" data-for-measure-id="<%= presenter.measure.measure_id %>">
|
||||
<% score_label_y = [5, 10, 15, 5, 10, 15 ] %>
|
||||
<% presenter.bars.each_with_index do |bar, index| %>
|
||||
<rect data-for-academic-year="<%= bar.academic_year.range %>" x="<%= bar.x_position %>%" y="<%= bar.y_offset %>%" width="<%= presenter.bar_width %>%" height="<%= bar.bar_height_percentage %>%" fill="<%= bar.color %>" />
|
||||
<g class="grouped-bar-column" data-for-measure-id="<%= presenter.measure.measure_id %>">
|
||||
<% score_label_y = [5, 10, 15, 5, 10, 15 ] %>
|
||||
<% presenter.bars.each_with_index do |bar, index| %>
|
||||
<rect data-for-academic-year="<%= bar.academic_year.range %>" x="<%= bar.x_position %>%" y="<%= bar.y_offset %>%" width="<%= presenter.bar_width %>%" height="<%= bar.bar_height_percentage %>%" fill="<%= bar.color %>" />
|
||||
|
||||
<% if ENV["SCORES"].present? && ENV["SCORES"].upcase == "SHOW" %>
|
||||
<text x="<%= bar.x_position + 3 %>%" y="<%= score_label_y[index] %>%" text-anchor="middle" dominant-baseline="middle" >
|
||||
<text x="<%= bar.x_position + 3 %>%" y="<%= score_label_y[index] %>%" text-anchor="middle" dominant-baseline="middle">
|
||||
<%= bar.average %>
|
||||
</text>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<text class="graph-footer" x="<%= presenter.column_midpoint %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle" data-grouped-bar-label="<%= presenter.label %>">
|
||||
<%= presenter.label %>
|
||||
</text>
|
||||
|
||||
<% if presenter.show_irrelevancy_message? %>
|
||||
<rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray"/>
|
||||
<rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray" />
|
||||
|
||||
<text x="<%= presenter.column_midpoint %>%" y="<%= 20 %>%" text-anchor="middle">
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="29%">measure not</tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="34%">based on</tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="39%"><%= presenter.basis %> surveys </tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="34%">based on</tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="39%"><%= presenter.basis %> surveys </tspan>
|
||||
</text>
|
||||
<% elsif presenter.show_insufficient_data_message? %>
|
||||
<rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray" />
|
||||
<rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray" />
|
||||
|
||||
<text x="<%= presenter.column_midpoint %>%" y="<%= 20 %>%" text-anchor="middle" >
|
||||
<text x="<%= presenter.column_midpoint %>%" y="<%= 20 %>%" text-anchor="middle">
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="29%">survey response</tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="34%">rate below 25%</tspan>
|
||||
<tspan x="<%= presenter.column_midpoint %>%" y="34%">rate below 25%</tspan>
|
||||
</text>
|
||||
<% end %>
|
||||
</g>
|
||||
|
|
|
|||
21
app/views/analyze/_school_years.html.erb
Normal file
21
app/views/analyze/_school_years.html.erb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<h3 class="sub-header-4 mt-5">School Years</h3>
|
||||
<% available_academic_years.each_with_index do | year, index | %>
|
||||
<div class="d-flex justify-content-start align-items-center mt-1" data-controller="analyze">
|
||||
<input type="checkbox"
|
||||
id="<%= year.range %>"
|
||||
name="year-checkbox"
|
||||
value="<%= analyze_subcategory_link(district: district, school: school, academic_year: academic_year, category: category, subcategory: subcategory) %>"
|
||||
<%= selected_academic_years.include?(year) ? "checked" : "" %>
|
||||
data-action="click->analyze#refresh"
|
||||
<%= empty_dataset?(measures: measures, school: school, academic_year: year) ? "disabled" : "" %>>
|
||||
|
||||
<label class="px-3" for="<%= year.range %>"><%= year.range %></label><br>
|
||||
<div class="bg-color-blue px-3" style="width:20px;height:20px;background-color:<%= colors[index] %>;"></div>
|
||||
<% if empty_dataset?(measures: measures, school: school, academic_year: year) %>
|
||||
<i class="fa-solid fa-circle-exclamation px-3"
|
||||
data-bs-toggle="popover" data-bs-placement="right"
|
||||
data-bs-content="Teacher and student survey response rates below <%= ResponseRateCalculator::TEACHER_RATE_THRESHOLD %>%">
|
||||
</i>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
@ -1,66 +1,33 @@
|
|||
<% content_for :title do %>
|
||||
<h1 class="sub-header-2 color-white m-0"> Analysis of <%= @school.name %> </h1>
|
||||
<% end %>
|
||||
|
||||
<div class="graph-content">
|
||||
<div class="breadcrumbs sub-header-4">
|
||||
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
||||
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
||||
</div>
|
||||
<hr/>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row pt-5 row">
|
||||
<div class="d-flex flex-column flex-grow-6 bg-color-white col-3 px-5" data-controller="analyze">
|
||||
<h3 class="sub-header-4">Focus Area</h3>
|
||||
<p>Select a category & subcategory to analyze measure-level results</p>
|
||||
<select id="select-category" class="mx-3 form-select" data-id="category-dropdown" data-action="analyze#refresh">
|
||||
<% @categories.each do |category| %>
|
||||
<option value="<%= analyze_category_link(district: @district, school: @school, academic_year: @academic_year, category: category) %>" <%= @category.id == category.id ? "selected": "" %> ><%= "#{category.category_id}: #{category.name}" %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<select id="select-subcategory" class="mx-3 form-select mt-3" data-id="subcategory-dropdown" data-action="analyze#refresh">
|
||||
<% @subcategories.each do |subcategory| %>
|
||||
<option value="<%= analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: subcategory) %>" <%= @subcategory.subcategory_id == subcategory.subcategory_id ? "selected": "" %>>
|
||||
<%= "#{subcategory.subcategory_id}: #{subcategory.name}" %>
|
||||
</option>
|
||||
<% end %>
|
||||
</select>
|
||||
<%= render partial: "focus_area", locals: {categories: @categories, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategories: @subcategories} %>
|
||||
|
||||
<h3 class="sub-header-4 mt-5">School Years</h3>
|
||||
<% @available_academic_years.each_with_index do | year, index | %>
|
||||
<div class="d-flex justify-content-start align-items-center mt-1" data-controller="analyze">
|
||||
<input type="checkbox"
|
||||
id="<%= year.range %>"
|
||||
name="year-checkbox"
|
||||
value="<%= analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory) %>"
|
||||
<%= @selected_academic_years.include?(year) ? "checked" : "" %>
|
||||
data-action="click->analyze#refresh"
|
||||
<%= empty_dataset?(measures: @measures, school: @school, academic_year: year) ? "disabled" : "" %>
|
||||
>
|
||||
<label class="px-3" for="<%= year.range %>" ><%= year.range %></label><br>
|
||||
<div class="bg-color-blue px-3" style="width:20px;height:20px;background-color:<%= colors[index] %>;"></div>
|
||||
<% if empty_dataset?(measures: @measures, school: @school, academic_year: year) %>
|
||||
<i class="fa-solid fa-circle-exclamation px-3"
|
||||
data-bs-toggle="popover" data-bs-placement="right"
|
||||
data-bs-content="Teacher and student survey response rates below <%= ResponseRateCalculator::TEACHER_RATE_THRESHOLD %>%" >
|
||||
</i>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render partial: "school_years", locals: {available_academic_years: @available_academic_years, selected_academic_years: @selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory, measures: @measures} %>
|
||||
|
||||
<%= 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] do %>
|
||||
<div class="bg-color-white flex-grow-1 col-9">
|
||||
<% @measures.each do |measure|%>
|
||||
<section class="mb-6" >
|
||||
<p class="construct-id">Measure <%= measure.measure_id %></p>
|
||||
<h2> <%= measure.name %> </h2>
|
||||
<% cache [@subcategory, @school, @selected_academic_years, @response_rate_timestamp] do %>
|
||||
<div class="bg-color-white flex-grow-1 col-9">
|
||||
<% @measures.each do |measure| %>
|
||||
<section class="mb-6">
|
||||
<p class="construct-id">Measure <%= measure.measure_id %></p>
|
||||
<h2> <%= measure.name %> </h2>
|
||||
|
||||
<%= render partial: "grouped_bar_chart" , locals: { measure: measure} %>
|
||||
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= render partial: "grouped_bar_chart" , locals: { measure: measure} %>
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
11
data/demographics.csv
Normal file
11
data/demographics.csv
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender
|
||||
1,American Indian or Alaskan Native,1,Male
|
||||
2,Asian or Pacific Islander,2,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,,
|
||||
6,Prefer not to disclose,,
|
||||
7,Prefer to self-describe,,
|
||||
8,Middle Eastern,,
|
||||
99,Unknown,,
|
||||
100,Multiracial,,
|
||||
|
12
db/migrate/20220720011653_create_races.rb
Normal file
12
db/migrate/20220720011653_create_races.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
class CreateRaces < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :races do |t|
|
||||
t.string :designation
|
||||
t.integer :qualtrics_code
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :races, :designation, unique: true
|
||||
add_index :races, :qualtrics_code, unique: true
|
||||
end
|
||||
end
|
||||
6
db/migrate/20220722030114_add_slug_to_race.rb
Normal file
6
db/migrate/20220722030114_add_slug_to_race.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class AddSlugToRace < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :races, :slug, :string
|
||||
add_index :races, :slug, unique: true
|
||||
end
|
||||
end
|
||||
15
db/schema.rb
15
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2022_06_16_220352) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2022_07_22_030114) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "plpgsql"
|
||||
|
|
@ -290,6 +290,17 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_16_220352) do
|
|||
t.index ["subcategory_id"], name: "index_measures_on_subcategory_id"
|
||||
end
|
||||
|
||||
create_table "races", force: :cascade do |t|
|
||||
t.string "designation"
|
||||
t.integer "qualtrics_code"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "slug"
|
||||
t.index ["designation"], name: "index_races_on_designation", unique: true
|
||||
t.index ["qualtrics_code"], name: "index_races_on_qualtrics_code", unique: true
|
||||
t.index ["slug"], name: "index_races_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "respondents", force: :cascade do |t|
|
||||
t.bigint "school_id", null: false
|
||||
t.bigint "academic_year_id", null: false
|
||||
|
|
@ -313,7 +324,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_16_220352) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.index ["academic_year_id"], name: "index_response_rates_on_academic_year_id"
|
||||
t.index ["school_id", "subcategory_id"], name: "index_response_rates_on_school_id_and_subcategory_id"
|
||||
t.index ["school_id"], name: "index_response_rates_on_school_id"
|
||||
t.index ["subcategory_id"], name: "index_response_rates_on_subcategory_id"
|
||||
end
|
||||
|
||||
|
|
@ -361,7 +371,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_16_220352) do
|
|||
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
|
||||
t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
|
||||
t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_id"
|
||||
t.index ["school_id"], name: "index_survey_item_responses_on_school_id"
|
||||
t.index ["survey_item_id"], name: "index_survey_item_responses_on_survey_item_id"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ seeder.seed_districts_and_schools Rails.root.join('data', 'master_list_of_school
|
|||
seeder.seed_surveys Rails.root.join('data', 'master_list_of_schools_and_districts.csv')
|
||||
seeder.seed_respondents Rails.root.join('data', 'master_list_of_schools_and_districts.csv')
|
||||
seeder.seed_sqm_framework Rails.root.join('data', 'sqm_framework.csv')
|
||||
seeder.seed_demographics Rails.root.join('data', 'demographics.csv')
|
||||
|
|
|
|||
11638
package-lock.json
generated
Normal file
11638
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -13,7 +13,6 @@
|
|||
"babel-preset-es2015": "^6.24.1",
|
||||
"bootstrap": "^5.1.3",
|
||||
"esbuild": "^0.13.6",
|
||||
"prettier": "^2.5.1",
|
||||
"sass": "^1.43.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
@ -25,7 +24,8 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"jest": "^27.2.5",
|
||||
"markdownlint": "^0.25.1"
|
||||
"markdownlint": "^0.25.1",
|
||||
"prettier": "2.7.1"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
FactoryBot.define do
|
||||
factory :race do
|
||||
designation { "MyString" }
|
||||
qualtrics_code { 1 }
|
||||
end
|
||||
|
||||
factory :response_rate do
|
||||
subcategory { nil }
|
||||
school { nil }
|
||||
|
|
|
|||
11
spec/fixtures/sample_demographics.csv
vendored
Normal file
11
spec/fixtures/sample_demographics.csv
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender
|
||||
1,American Indian or Alaskan Native,1,Male
|
||||
2,Asian or Pacific Islander,2,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,,
|
||||
6,Prefer not to disclose,,
|
||||
7,Prefer to self-describe,,
|
||||
8,Middle Eastern,,
|
||||
99,Unknown,,
|
||||
100,Multiracial,,
|
||||
|
|
|
@ -1,5 +1,34 @@
|
|||
require 'rails_helper'
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
RSpec.describe AdminDataValue, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
let(:school) { create(:school) }
|
||||
let(:admin_data_item) { create(:admin_data_item) }
|
||||
let(:academic_year) { create(:academic_year) }
|
||||
context '#value' do
|
||||
context 'when the value is in the valid range of greater than zero to five' do
|
||||
it 'should return valid values' do
|
||||
value = AdminDataValue.create!(likert_score: 1, school:, admin_data_item:, academic_year:)
|
||||
expect(value.likert_score).to eq(1)
|
||||
value = AdminDataValue.create!(likert_score: 2, school:, admin_data_item:, academic_year:)
|
||||
expect(value.likert_score).to eq(2)
|
||||
value = AdminDataValue.create!(likert_score: 5, school:, admin_data_item:, academic_year:)
|
||||
expect(value.likert_score).to eq(5)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value is zero or below or greater than 5' do
|
||||
it 'should not create the value' do
|
||||
expect do
|
||||
AdminDataValue.create!(likert_score: 0, school:, admin_data_item:,
|
||||
academic_year:)
|
||||
end.to raise_error
|
||||
expect do
|
||||
AdminDataValue.create!(likert_score: 5.00001, school:, admin_data_item:,
|
||||
academic_year:)
|
||||
end.to raise_error
|
||||
expect(AdminDataValue.count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
5
spec/models/race_spec.rb
Normal file
5
spec/models/race_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Race, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
22
spec/services/demographic_loader_spec.rb
Normal file
22
spec/services/demographic_loader_spec.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe DemographicLoader do
|
||||
let(:filepath) { 'spec/fixtures/sample_demographics.csv' }
|
||||
|
||||
before :each do
|
||||
DemographicLoader.load_data(filepath:)
|
||||
end
|
||||
|
||||
after :each do
|
||||
DatabaseCleaner.clean
|
||||
end
|
||||
|
||||
describe 'self.load_data' do
|
||||
it 'does not load qualtrics categories for `prefer not to disclose` or `prefer to self-describe`' do
|
||||
expect(Race.find_by_qualtrics_code(6)).to be nil
|
||||
end
|
||||
it 'loads all racial designations' do
|
||||
expect(Race.all.count).to eq 8
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,6 +7,14 @@ describe 'analyze/index' do
|
|||
let(:subcategory) { create(:subcategory, category:) }
|
||||
let(:school) { create(:school) }
|
||||
let(:academic_year) { create(:academic_year) }
|
||||
let(:races) do
|
||||
DemographicLoader.load_data(filepath: 'spec/fixtures/sample_demographics.csv')
|
||||
Race.all
|
||||
end
|
||||
let(:graphs) do
|
||||
[AnalysisGraph::StudentsAndTeachers.new, AnalysisGraph::StudentsByGroup.new]
|
||||
end
|
||||
let(:selected_races) { races }
|
||||
|
||||
let(:support_for_teaching) do
|
||||
measure = create(:measure, name: 'Support For Teaching Development & Growth', measure_id: '1A-I', subcategory:)
|
||||
|
|
@ -45,6 +53,9 @@ describe 'analyze/index' do
|
|||
end
|
||||
|
||||
before :each do
|
||||
assign :races, races
|
||||
assign :selected_races, selected_races
|
||||
assign :graphs, graphs
|
||||
assign :academic_year, academic_year
|
||||
assign :available_academic_years, [academic_year]
|
||||
assign :selected_academic_years, [academic_year]
|
||||
|
|
@ -57,10 +68,11 @@ describe 'analyze/index' do
|
|||
assign :measures, [support_for_teaching, effective_leadership, professional_qualifications]
|
||||
create(:respondent, school:, academic_year:)
|
||||
create(:survey, school:, academic_year:)
|
||||
|
||||
render
|
||||
end
|
||||
context 'when all the presenters have a nil score' do
|
||||
before do
|
||||
render
|
||||
end
|
||||
# let(:grouped_bar_column_presenters) do
|
||||
# measure = create(:measure, name: 'Display Me', measure_id: 'display-me')
|
||||
# scale = create(:scale, measure:)
|
||||
|
|
@ -83,8 +95,8 @@ describe 'analyze/index' do
|
|||
displayed_variance_rows = subject.css('[data-for-measure-id]')
|
||||
expect(displayed_variance_rows.first.attribute('data-for-measure-id').value).to eq '1A-I'
|
||||
|
||||
# displayed_academic_years = subject.css('[data-for-academic-year]')
|
||||
# expect(displayed_academic_years.count).to eq 9
|
||||
displayed_academic_years = subject.css('[data-for-academic-year]')
|
||||
expect(displayed_academic_years.count).to eq 0
|
||||
|
||||
displayed_variance_labels = subject.css('[data-grouped-bar-label]')
|
||||
expect(displayed_variance_labels.count).to eq 9
|
||||
|
|
@ -112,5 +124,29 @@ describe 'analyze/index' do
|
|||
expect(academic_year.range).to eq '2050-51'
|
||||
expect(year_checkbox).to have_attribute 'disabled'
|
||||
end
|
||||
|
||||
it 'displays a radio box selector for each type of data filter' do
|
||||
expect(subject).to have_css '#students-and-teachers'
|
||||
expect(subject).to have_css '#students-by-group'
|
||||
end
|
||||
|
||||
it 'displays a checkbox for each race designation' do
|
||||
race_slugs = %w[american-indian-or-alaskan-native asian-or-pacific-islander black-or-african-american
|
||||
hispanic-or-latinx middle-eastern multiracial unknown white-or-caucasian]
|
||||
race_slugs.each do |slug|
|
||||
expect(subject).to have_css("//input[@type='checkbox'][@id='#{slug}']")
|
||||
end
|
||||
|
||||
expect(subject.css("//input[@type='checkbox'][@id='american-indian-or-alaskan-native']")).to have_checked_field
|
||||
end
|
||||
end
|
||||
|
||||
context 'when presenters have a displayable score' do
|
||||
before do
|
||||
render
|
||||
end
|
||||
|
||||
context 'when displaying a student and teacher graph' do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue