chore: start adding browse view

main
Nelson Jovel 2 years ago
parent a538eb72f2
commit f71f88a4ac

@ -1,8 +1,10 @@
# frozen_string_literal: true
class AnalyzeController < SqmApplicationController
def index
@presenter = Analyze::Presenter.new(params:, school: @school, academic_year: @academic_year)
@background ||= BackgroundPresenter.new(num_of_columns: @presenter.graph.columns.count)
module Dashboard
class AnalyzeController < SqmApplicationController
def index
@presenter = Analyze::Presenter.new(params:, school: @school, academic_year: @academic_year)
@background ||= BackgroundPresenter.new(num_of_columns: @presenter.graph.columns.count)
end
end
end

@ -1,11 +1,13 @@
# frozen_string_literal: true
class CategoriesController < SqmApplicationController
helper GaugeHelper
module Dashboard
class CategoriesController < SqmApplicationController
helper GaugeHelper
def show
@categories = Category.sorted.map { |category| CategoryPresenter.new(category:) }
def show
@categories = Category.sorted.map { |category| CategoryPresenter.new(category:) }
@category = CategoryPresenter.new(category: Category.find_by_slug(params[:id]))
@category = CategoryPresenter.new(category: Category.find_by_slug(params[:id]))
end
end
end

@ -2,98 +2,99 @@
Point = Struct.new(:x, :y)
Rect = Struct.new(:x, :y, :width, :height)
module GaugeHelper
def outer_radius
100
end
def inner_radius
50
end
def stroke_width
1
end
def effective_radius
outer_radius + stroke_width
end
def diameter
2 * effective_radius
end
def width
diameter
end
def height
outer_radius + 2 * stroke_width + key_benchmark_indicator_gutter
end
def key_benchmark_indicator_gutter
10
end
def viewbox
x = arc_center.x - effective_radius
y = arc_center.y - effective_radius - key_benchmark_indicator_gutter
Rect.new(x, y, width, height)
end
def arc_center
Point.new(0, 0)
end
def arc_radius(radius)
"#{radius} #{radius}"
end
def angle_for(percentage:)
-Math::PI * (1 - percentage)
end
def arc_end_point_for(radius:, percentage:)
angle = angle_for(percentage:)
x = arc_center.x + radius * Math.cos(angle)
y = arc_center.y + radius * Math.sin(angle)
Point.new(x, y)
end
def arc_end_line_destination(radius:, percentage:)
angle = angle_for(percentage:)
x = arc_center.x + radius * Math.cos(angle)
y = arc_center.y + radius * Math.sin(angle)
Point.new(x, y)
end
def arc_start_point
Point.new(arc_center.x - outer_radius, arc_center.y)
end
def move_to(point:)
"M #{coordinates_for(point)}"
end
def draw_arc(radius:, percentage:, clockwise:)
sweep_flag = clockwise ? 1 : 0
"A #{arc_radius(radius)} 0 0 #{sweep_flag} #{coordinates_for(arc_end_point_for(radius:,
percentage:))}"
end
def draw_line_to(point:)
"L #{coordinates_for(point)}"
end
def benchmark_line_point(radius, angle)
x = (radius * Math.cos(angle)).to_s
y = (radius * Math.sin(angle) + arc_center.y).to_s
Point.new(x, y)
end
def coordinates_for(point)
"#{point.x} #{point.y}"
module Dashboard
module GaugeHelper
def outer_radius
100
end
def inner_radius
50
end
def stroke_width
1
end
def effective_radius
outer_radius + stroke_width
end
def diameter
2 * effective_radius
end
def width
diameter
end
def height
outer_radius + 2 * stroke_width + key_benchmark_indicator_gutter
end
def key_benchmark_indicator_gutter
10
end
def viewbox
x = arc_center.x - effective_radius
y = arc_center.y - effective_radius - key_benchmark_indicator_gutter
Rect.new(x, y, width, height)
end
def arc_center
Point.new(0, 0)
end
def arc_radius(radius)
"#{radius} #{radius}"
end
def angle_for(percentage:)
-Math::PI * (1 - percentage)
end
def arc_end_point_for(radius:, percentage:)
angle = angle_for(percentage:)
x = arc_center.x + radius * Math.cos(angle)
y = arc_center.y + radius * Math.sin(angle)
Point.new(x, y)
end
def arc_end_line_destination(radius:, percentage:)
angle = angle_for(percentage:)
x = arc_center.x + radius * Math.cos(angle)
y = arc_center.y + radius * Math.sin(angle)
Point.new(x, y)
end
def arc_start_point
Point.new(arc_center.x - outer_radius, arc_center.y)
end
def move_to(point:)
"M #{coordinates_for(point)}"
end
def draw_arc(radius:, percentage:, clockwise:)
sweep_flag = clockwise ? 1 : 0
"A #{arc_radius(radius)} 0 0 #{sweep_flag} #{coordinates_for(arc_end_point_for(radius:,
percentage:))}"
end
def draw_line_to(point:)
"L #{coordinates_for(point)}"
end
def benchmark_line_point(radius, angle)
x = (radius * Math.cos(angle)).to_s
y = (radius * Math.sin(angle) + arc_center.y).to_s
Point.new(x, y)
end
def coordinates_for(point)
"#{point.x} #{point.y}"
end
end
end

@ -1,8 +1,8 @@
module Dashboard
class Race < ApplicationRecord
include FriendlyId
has_many :dashboard_student_races
has_many :dashboard_students, through: :student_races
has_and_belongs_to_many :students, join_table: :dashboard_student_races, class_name: "Student",
foreign_key: :dashboard_student_id, association_foreign_key: :dashboard_student_id
friendly_id :designation, use: [:slugged]

@ -1,8 +1,8 @@
module Dashboard
class Student < ApplicationRecord
# has_many :dashboard_survey_item_responses
has_many :dashboard_student_races
has_and_belongs_to_many :races, join_table: :student_races
has_and_belongs_to_many :races, join_table: :dashboard_student_races, class_name: "Race",
foreign_key: :dashboard_race_id, association_foreign_key: :dashboard_race_id
encrypts :lasid, deterministic: true
end

@ -54,7 +54,7 @@ module Dashboard
def self.grouped_responses(school:, academic_year:)
@grouped_responses ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] =
SurveyItemResponse.where(school:, academic_year:).group(:survey_item_id).average(:likert_score)
SurveyItemResponse.where(school:, academic_year:).group(:dashboard_survey_item_id).average(:likert_score)
end
@grouped_responses[[school, academic_year]]
end

@ -0,0 +1,42 @@
<div class="accordion-item">
<h3 class="accordion-header measure-accordion-header" id="<%= data_item_section.id %>-header">
<button
class="accordion-button measure-accordion-button collapsed"
data-bs-toggle="collapse"
data-bs-target="#<%= data_item_section.id %>"
aria-expanded="false"
aria-controls="<%= data_item_section.id %>"
>
<%= data_item_section.title %>
<% unless data_item_section.sufficient_data? %>
&nbsp;<i class="fa-solid fa-circle-exclamation" data-exclamation-point="<%= data_item_section.id %>"></i>
<% end %>
</button>
</h3>
<div
id="<%= data_item_section.id %>"
class="accordion-collapse collapse"
aria-labelledby="<%= data_item_section.id %>-header"
data-bs-parent="#<%= data_item_section.data_item_accordion_id %>"
>
<div class="accordion-body measure-accordion-body font-cabin font-size-14 weight-400">
<% unless data_item_section.sufficient_data? %>
<div class="alert alert-secondary" role="alert" data-insufficient-data-message="<%= data_item_section.id + '-' + data_item_section.reason_for_insufficiency %>">
Data not included due to <%= data_item_section.reason_for_insufficiency %>
</div>
<% end %>
<ul>
<% data_item_section.descriptions_and_availability.each do |data| %>
<li><%= data.description %>
<% unless data.available? %>
&nbsp;<i class="fa-solid fa-circle-exclamation" data-missing-data="<%= data.id %>"
data-bs-toggle="popover" data-bs-placement="right"
data-bs-content="Data not included due to limited availability"></i>
<% end %>
</li>
<% end %>
</ul>
</div>
</div>
</div>

@ -0,0 +1,59 @@
<div class="d-flex flex-column align-items-center position-relative">
<% if ENV["SCORES"].present? && ENV["SCORES"].upcase == "SHOW" %>
<p>Score is : <%= gauge.score %> </p>
<% end %>
<svg
viewBox="<%= viewbox.x %> <%= viewbox.y %> <%= viewbox.width %> <%= viewbox.height %>"
class="<%= gauge_class %>"
>
<% if gauge.score_percentage.present? %>
<path
class="gauge-fill <%= gauge.color_class %>"
d="<%= move_to(point: arc_start_point) %>
<%= draw_arc(radius: outer_radius, percentage: gauge.score_percentage, clockwise: true) %>
<%= draw_line_to(point: arc_end_line_destination(radius: inner_radius, percentage: gauge.score_percentage)) %>
<%= draw_arc(radius: inner_radius, percentage: 0, clockwise: false) %>
<%= draw_line_to(point: arc_end_line_destination(radius: outer_radius, percentage: 0)) %>"
fill="none"
stroke="none"
/>
<% end %>
<path
class="gauge-outline stroke-gray-2"
d="<%= move_to(point: arc_start_point) %>
<%= draw_arc(radius: outer_radius, percentage: 1, clockwise: true) %>
<%= draw_line_to(point: arc_end_line_destination(radius: inner_radius, percentage: 1)) %>
<%= draw_arc(radius: inner_radius, percentage: 0, clockwise: false) %>
<%= draw_line_to(point: arc_end_line_destination(radius: outer_radius, percentage: 0)) %>"
fill="none"
stroke-width="<%= stroke_width %>"
/>
<% benchmark_boundaries = [:watch_low, :growth_low, :ideal_low]%>
<% benchmark_boundaries.each do |zone| %>
<line
class="zone-benchmark stroke-gray-2"
x1="<%= benchmark_line_point(outer_radius, angle_for(percentage: gauge.boundary_percentage_for(zone))).x %>"
y1="<%= benchmark_line_point(outer_radius, angle_for(percentage: gauge.boundary_percentage_for(zone))).y %>"
x2="<%= benchmark_line_point(inner_radius, angle_for(percentage: gauge.boundary_percentage_for(zone))).x %>"
y2="<%= benchmark_line_point(inner_radius, angle_for(percentage: gauge.boundary_percentage_for(zone))).y %>"
stroke-width="<%= stroke_width %>"
/>
<% end %>
<% if gauge.key_benchmark_percentage.present? %>
<line
class="zone-benchmark stroke-black"
x1="<%= benchmark_line_point(outer_radius + 5, angle_for(percentage: gauge.key_benchmark_percentage)).x %>"
y1="<%= benchmark_line_point(outer_radius + 5, angle_for(percentage: gauge.key_benchmark_percentage)).y %>"
x2="<%= benchmark_line_point(inner_radius - 5 , angle_for(percentage: gauge.key_benchmark_percentage)).x %>"
y2="<%= benchmark_line_point(inner_radius - 5, angle_for(percentage: gauge.key_benchmark_percentage)).y %>"
stroke-width="<%= stroke_width + 2 %>"
/>
<% end %>
</svg>
<span class="gauge-title <%= font_class %> fill-black"><%= gauge.title %></span>
</div>

@ -0,0 +1,12 @@
<div id="<%= measure_presenter.id %>" class="measure-section mx-4">
<p class="construct-id">Measure <%= measure_presenter.id %></p>
<h3 class="measure-description sub-header-4 mb-5 "><%= measure_presenter.name %></h3>
<div>
<%= render partial: "gauge_graph", locals: { gauge: measure_presenter.gauge_presenter, gauge_class: 'gauge-graph-sm', font_class: 'weight-700' } %>
</div>
<p class="measure-description body-small mt-5 mb-4"><%= measure_presenter.description %></p>
<div class="measure-accordion accordion" id="<%= measure_presenter.data_item_accordion_id %>">
<%= render partial: "data_item_section", collection: measure_presenter.data_item_presenters %>
</div>
</div>

@ -0,0 +1,59 @@
<section class="subcategory-section">
<div id="<%= subcategory.id %>" class="p-7">
<p class="construct-id">Subcategory <%= subcategory.id %></p>
<h2 class="sub-header-2 font-bitter mb-7"><%= subcategory.name %></h2>
<div class="d-flex justify-content-between align-items-end">
<div>
<%= render partial: "gauge_graph", locals: { gauge: subcategory.gauge_presenter, gauge_class: 'gauge-graph-lg', font_class: 'sub-header-3' } %>
</div>
<div class="d-flex flex-column mx-7">
<p class="body-large "><%= subcategory.description %></p>
<div class="d-flex justify-content-start">
<div
class="body-large text-center response-rate"
data-bs-toggle="popover"
data-bs-trigger="hover focus"
data-bs-content="The number of publicly available school data sources, often collected from the MA Department of Elementary and Secondary Education."
data-bs-placement="bottom"
>
<p class="response-rate-percentage"><%= subcategory.admin_collection_rate.first %> / <%= subcategory.admin_collection_rate.last %></p>
<p>school admin data sources</p>
</div>
<div
class="body-large mx-3 text-center response-rate"
data-bs-toggle="popover"
data-bs-trigger="hover focus"
data-bs-content="The student survey response rate for this sub-category. This number differs from the overall response rate because each individual student receives 44 of 67 total questions, in order to avoid survey fatigue."
data-bs-placement="bottom"
>
<p class="response-rate-percentage"><%= subcategory.student_response_rate %></p>
<p>of students responded</p>
</div>
<div
class="body-large text-center response-rate"
data-bs-toggle="popover"
data-bs-trigger="hover focus"
data-bs-content="The teacher survey response rate for this sub-category. This number differs from the overall response rate because the survey includes skip logic to limit the number of questions for each individual survey respondent."
data-bs-placement="bottom"
>
<p class="response-rate-percentage"><%= subcategory.teacher_response_rate %></p>
<p>of teachers responded</p>
</div>
</div>
</div>
</div>
</div>
<div class="arrow-container mt-7">
<div class="arrow-shadow"></div>
<div class="arrow"></div>
</div>
<div class="measure-card d-flex p-7">
<% subcategory.measure_presenters.each do |measure_presenter| %>
<%= render partial: "measures_section", locals: { measure_presenter: measure_presenter } %>
<% end %>
</div>
</section>

@ -0,0 +1,25 @@
<% content_for :navigation do %>
<nav class="nav nav-tabs align-self-end">
<% @categories.each do |category| %>
<div class="nav-item">
<%= link_to [@district, @school, category, { year: @academic_year.range }], class: ["nav-link", current_page?([@district, @school, category, { year: @academic_year.range }]) ? "active" : ""] do %>
<i class="<%= category.icon_class %> <%= category.icon_color_class if current_page?([@district, @school, category, { year: @academic_year.range }]) %> me-2" ></i>
<%= category.name %>
<% end %>
</div>
<% end %>
</nav>
<select id="select-academic-year" class="form-select" name="academic-year">
<% @academic_years.each do |year| %>
<option value="<%= url_for [@district, @school, @category , {year: year.range} ]%>" <%= @academic_year == year ? "selected" : nil %>><%= year.formatted_range %></option>
<% end %>
</select>
<% end %>
<% cache [@category, @school, @academic_year] do %>
<p class="construct-id">Category <%= @category.id %></p>
<h1 class="sub-header font-bitter color-red"><%= @category.name %></h1>
<p class="col-8 body-large"><%= @category.description %></p>
<% @category.subcategories(academic_year: @academic_year, school: @school).each do |subcategory| %>
<%= render partial: "subcategory_section", locals: {subcategory: subcategory} %>
<% end %>
<% end %>

@ -0,0 +1,7 @@
module StringMonkeyPatches
def valid_likert_score?
to_i.between? 1, 5
end
end
String.include StringMonkeyPatches
Loading…
Cancel
Save