parent
435bc4a5be
commit
a71ebbc4e4
@ -1,107 +1,107 @@
|
||||
source "https://rubygems.org"
|
||||
ruby "3.2.1"
|
||||
source 'https://rubygems.org'
|
||||
ruby '3.2.1'
|
||||
|
||||
git_source(:github) do |repo_name|
|
||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
|
||||
"https://github.com/#{repo_name}.git"
|
||||
end
|
||||
|
||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||
gem "rails", "~> 7.0.4"
|
||||
gem "sprockets-rails"
|
||||
gem 'rails', '~> 7.0.4'
|
||||
gem 'sprockets-rails'
|
||||
|
||||
gem "pg"
|
||||
gem 'pg'
|
||||
|
||||
# Use Puma as the app server
|
||||
gem "puma", ">= 5.6.4"
|
||||
gem 'puma', '>= 5.6.4'
|
||||
# Use Uglifier as compressor for JavaScript assets
|
||||
gem "uglifier", ">= 1.3.0"
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
# See https://github.com/rails/execjs#readme for more supported runtimes
|
||||
# Use jquery as the JavaScript library
|
||||
gem "jquery-rails"
|
||||
gem 'jquery-rails'
|
||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||
gem "jbuilder", "~> 2.5"
|
||||
gem 'jbuilder', '~> 2.5'
|
||||
# Use Redis adapter to run Action Cable in production
|
||||
gem "redis", "~> 3.0"
|
||||
gem 'redis', '~> 3.0'
|
||||
# Use ActiveModel has_secure_password
|
||||
# gem 'bcrypt', '~> 3.1.7'
|
||||
|
||||
gem "nokogiri", ">= 1.13.4"
|
||||
gem 'nokogiri', '>= 1.13.4'
|
||||
|
||||
gem "bootsnap", require: false
|
||||
gem 'bootsnap', require: false
|
||||
|
||||
gem "haml"
|
||||
gem 'haml'
|
||||
|
||||
gem "friendly_id", "~> 5.1.0"
|
||||
gem 'friendly_id', '~> 5.1.0'
|
||||
|
||||
gem "newrelic_rpm"
|
||||
gem 'newrelic_rpm'
|
||||
|
||||
gem "devise"
|
||||
gem 'devise'
|
||||
|
||||
gem "omniauth"
|
||||
gem 'omniauth'
|
||||
|
||||
gem "twilio-ruby", "~> 4.11.1"
|
||||
gem 'twilio-ruby', '~> 4.11.1'
|
||||
|
||||
gem "activerecord-import"
|
||||
gem 'activerecord-import'
|
||||
|
||||
gem "jsbundling-rails"
|
||||
gem 'jsbundling-rails'
|
||||
|
||||
gem "cssbundling-rails"
|
||||
gem 'cssbundling-rails'
|
||||
|
||||
gem "turbo-rails"
|
||||
gem 'turbo-rails'
|
||||
|
||||
gem "stimulus-rails"
|
||||
gem 'stimulus-rails'
|
||||
|
||||
gem "watir"
|
||||
gem 'watir'
|
||||
|
||||
gem "selenium-webdriver", "~> 4.4"
|
||||
gem "net-sftp"
|
||||
gem "ed25519"
|
||||
gem "bcrypt_pbkdf"
|
||||
gem 'selenium-webdriver', '~> 4.4'
|
||||
gem 'net-sftp'
|
||||
gem 'ed25519'
|
||||
gem 'bcrypt_pbkdf'
|
||||
|
||||
gem "standard_deviation"
|
||||
gem 'standard_deviation'
|
||||
|
||||
group :development, :test do
|
||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||
gem "byebug", platform: :mri
|
||||
gem "factory_bot_rails"
|
||||
gem "parallel_tests"
|
||||
gem "rack-mini-profiler"
|
||||
gem "rspec-rails", "~> 5.1.0"
|
||||
gem "debug", platforms: %i[mri mingw x64_mingw]
|
||||
gem 'byebug', platform: :mri
|
||||
gem 'factory_bot_rails'
|
||||
gem 'parallel_tests'
|
||||
gem 'rack-mini-profiler'
|
||||
gem 'rspec-rails', '~> 5.1.0'
|
||||
gem 'debug', platforms: %i[mri mingw x64_mingw]
|
||||
end
|
||||
|
||||
group :development do
|
||||
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
|
||||
gem "brakeman"
|
||||
gem "bullet"
|
||||
gem "erb_lint", require: false
|
||||
gem "erblint-github"
|
||||
gem "guard"
|
||||
gem "guard-rspec", require: false
|
||||
gem "guard-livereload", "~> 2.5", require: false
|
||||
gem "rack-livereload"
|
||||
gem "listen", "~> 3.0.5"
|
||||
gem "nested_scaffold"
|
||||
gem 'brakeman'
|
||||
gem 'bullet'
|
||||
gem 'erb_lint', require: false
|
||||
gem 'erblint-github'
|
||||
gem 'guard'
|
||||
gem 'guard-rspec', require: false
|
||||
gem 'guard-livereload', '~> 2.5', require: false
|
||||
gem 'rack-livereload'
|
||||
gem 'listen', '~> 3.0.5'
|
||||
gem 'nested_scaffold'
|
||||
# gem 'reek', require: false
|
||||
gem "rubocop", require: false
|
||||
gem "seed_dump"
|
||||
gem "solargraph-reek"
|
||||
gem "spring"
|
||||
gem "web-console"
|
||||
gem 'rubocop', require: false
|
||||
gem 'seed_dump'
|
||||
gem 'solargraph-reek'
|
||||
gem 'spring'
|
||||
gem 'web-console'
|
||||
end
|
||||
|
||||
group "test" do
|
||||
gem "apparition", github: "twalpole/apparition", ref: "ca86be4d54af835d531dbcd2b86e7b2c77f85f34"
|
||||
gem "capybara"
|
||||
gem "database_cleaner"
|
||||
gem "launchy"
|
||||
gem "rails-controller-testing"
|
||||
gem "simplecov", require: false
|
||||
gem "timecop"
|
||||
group 'test' do
|
||||
gem 'apparition', github: 'twalpole/apparition', ref: 'ca86be4d54af835d531dbcd2b86e7b2c77f85f34'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner'
|
||||
gem 'launchy'
|
||||
gem 'rails-controller-testing'
|
||||
gem 'simplecov', require: false
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
|
||||
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
||||
|
||||
gem "reline", "~> 0.3.2"
|
||||
gem 'reline', '~> 0.3.2'
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
class ResponseRatePresenter
|
||||
attr_reader :focus, :academic_year, :school, :survey_items
|
||||
|
||||
def initialize(focus:, academic_year:, school:)
|
||||
@focus = focus
|
||||
@academic_year = academic_year
|
||||
@school = school
|
||||
@survey_items = SurveyItem.student_survey_items if focus == :student
|
||||
@survey_items = SurveyItem.teacher_survey_items if focus == :teacher
|
||||
end
|
||||
|
||||
def date
|
||||
SurveyItemResponse.where(survey_item: survey_items, school:).order(updated_at: :DESC).first&.updated_at || Date.new
|
||||
end
|
||||
|
||||
def percentage
|
||||
cap_at_100(actual_count.to_f / respondents_count.to_f * 100).round
|
||||
end
|
||||
|
||||
def color
|
||||
# Problem: the color (either $gold or $purple) is determined by the scss variable, but the
|
||||
# percentage is decided by the presenter. Therefore the class style must be generated
|
||||
# within this file and not the scss file.
|
||||
# TODO: Fix this.
|
||||
percentage > 75 ? '#49416D' : '#FFC857'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cap_at_100(value)
|
||||
value > 100 ? 100 : value
|
||||
end
|
||||
|
||||
def actual_count
|
||||
SurveyItemResponse.where(school:, academic_year:,
|
||||
survey_item: survey_items).select(:response_id).distinct.count
|
||||
end
|
||||
|
||||
def respondents_count
|
||||
respondents = Respondent.find_by(school:, academic_year:)
|
||||
count = respondents.total_students if focus == :student
|
||||
count = respondents.total_teachers if focus == :teacher
|
||||
count
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,8 @@
|
||||
<div class="overall-response-rate-container">
|
||||
<div>Response Rates as of <%= response_rate_presenter.date.to_date.strftime("%m/%d/%y") %> </div>
|
||||
<div style="display: flex; justify-content:space-between; width: 100px;">
|
||||
<div><%= response_rate_presenter.focus.capitalize %> </div>
|
||||
<%= render partial: "response_rate_graphic", locals: {response_rate_presenter: response_rate_presenter}, cached: true %>
|
||||
<div><%= response_rate_presenter.percentage %>% </div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,35 @@
|
||||
<style>
|
||||
/*
|
||||
For some reason, none of the sizing in the pie class works, and it always
|
||||
fills 100% of the containing frame, so the size has to be dictated by .prog
|
||||
*/
|
||||
.prog {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
border: 1px solid black;
|
||||
border-radius: 50%;
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
.pie {
|
||||
aspect-ratio: 1;
|
||||
display: inline-grid;
|
||||
place-content: center;
|
||||
margin: 5px;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#response-rate-pie-<%= response_rate_presenter.focus %>:before{
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
inset: 0;
|
||||
background: conic-gradient(<%= response_rate_presenter.color %> calc(<%= response_rate_presenter.percentage %>*1%),#0000 0);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="prog">
|
||||
<div id="response-rate-pie-<%= response_rate_presenter.focus %>" class="pie"></div>
|
||||
</div>
|
||||
@ -0,0 +1,5 @@
|
||||
class AddRecordedDateToSurveyItemResponse < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :survey_item_responses, :recorded_date, :datetime
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,160 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ResponseRatePresenter do
|
||||
let(:academic_year) { create(:academic_year, range: '2022-23') }
|
||||
let(:school) { create(:school, name: 'A school') }
|
||||
let(:respondents) { create(:respondent, school:, academic_year:, total_students: 40, total_teachers: 40) }
|
||||
let(:wrong_school) { create(:school, name: 'Wrong school') }
|
||||
let(:wrong_academic_year) { create(:academic_year) }
|
||||
let(:wrong_respondents) do
|
||||
create(:respondent, school: wrong_school, academic_year: wrong_academic_year, total_students: 40,
|
||||
total_teachers: 40)
|
||||
end
|
||||
|
||||
let(:student_survey_item) { create(:student_survey_item) }
|
||||
let(:teacher_survey_item) { create(:teacher_survey_item) }
|
||||
let(:oldest_student_survey_response) do
|
||||
create(:survey_item_response, school:, academic_year:, survey_item: student_survey_item)
|
||||
end
|
||||
let(:newest_student_survey_response) do
|
||||
create(:survey_item_response, school:, academic_year:, survey_item: student_survey_item)
|
||||
end
|
||||
let(:oldest_teacher_survey_response) do
|
||||
create(:survey_item_response, school:, academic_year:, survey_item: teacher_survey_item)
|
||||
end
|
||||
let(:newest_teacher_survey_response) do
|
||||
create(:survey_item_response, school:, academic_year:, survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
let(:wrong_student_survey_response) do
|
||||
create(:survey_item_response, school: wrong_school, academic_year: wrong_academic_year,
|
||||
survey_item: student_survey_item)
|
||||
end
|
||||
let(:wrong_teacher_survey_response) do
|
||||
create(:survey_item_response, school: wrong_school, academic_year: wrong_academic_year,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
context '.date' do
|
||||
context 'when focus is student' do
|
||||
before :each do
|
||||
oldest_student_survey_response
|
||||
newest_student_survey_response
|
||||
wrong_student_survey_response
|
||||
wrong_teacher_survey_response
|
||||
end
|
||||
|
||||
it 'ignores all teacher items and only gets the modified date of the last student item' do
|
||||
rdate = ResponseRatePresenter.new(focus: :student, academic_year:, school:).date
|
||||
expect(rdate).to eq(newest_student_survey_response.updated_at)
|
||||
end
|
||||
end
|
||||
context 'when focus is teacher' do
|
||||
before :each do
|
||||
oldest_teacher_survey_response
|
||||
newest_teacher_survey_response
|
||||
wrong_student_survey_response
|
||||
wrong_teacher_survey_response
|
||||
end
|
||||
|
||||
it 'ignores all student responses and only gets the modified date of the last teacher item' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).date
|
||||
expect(rdate).to eq(newest_teacher_survey_response.updated_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.percentage' do
|
||||
before :each do
|
||||
respondents
|
||||
wrong_respondents
|
||||
end
|
||||
context 'when no survey responses are found for a school' do
|
||||
it 'returns a response rate of 0' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there all possible teacher respondents answered questions' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 40, school:, academic_year:,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 100' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(100)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when more teachers responded than staff the school' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 80, school:, academic_year:,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 100' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(100)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when three quarters of the teachers responded to the survey' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 30, school:, academic_year:,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 75' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(75)
|
||||
end
|
||||
end
|
||||
context 'when one quarter of the teachers responded to the survey' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 10, school:, academic_year:,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 25' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(25)
|
||||
end
|
||||
end
|
||||
context 'When the percentage is not a round number' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 9, school:, academic_year:,
|
||||
survey_item: teacher_survey_item)
|
||||
end
|
||||
|
||||
it 'its rounded to the nearest integer' do
|
||||
rdate = ResponseRatePresenter.new(focus: :teacher, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(23)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there all possible student respondents answered questions' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 40, school:, academic_year:,
|
||||
survey_item: student_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 100' do
|
||||
rdate = ResponseRatePresenter.new(focus: :student, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(100)
|
||||
end
|
||||
end
|
||||
context 'when half of all students responded' do
|
||||
before :each do
|
||||
create_list(:survey_item_response, 20, school:, academic_year:,
|
||||
survey_item: student_survey_item)
|
||||
end
|
||||
|
||||
it 'returns a response rate of 50' do
|
||||
rdate = ResponseRatePresenter.new(focus: :student, academic_year:, school:).percentage
|
||||
expect(rdate).to eq(50)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in new issue