mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-08 23:18:18 -07:00
Merge pull request #1 from jaredcosulich/recipient_students
Recipient students
This commit is contained in:
commit
0c2aa09ebc
17 changed files with 353 additions and 46 deletions
|
|
@ -14,15 +14,20 @@ class AttemptsController < ApplicationController
|
|||
end
|
||||
|
||||
attempt.save_response(
|
||||
answer_index: twilio_params[:Body].to_i,
|
||||
answer_index: twilio_params[:Body].to_i > 0 ? twilio_params[:Body].to_i : nil,
|
||||
twilio_details: twilio_params.to_h.to_yaml
|
||||
)
|
||||
|
||||
if (twilio_params[:Body].downcase == 'skip')
|
||||
render plain: 'Thank you, this question has been skipped.'
|
||||
return
|
||||
end
|
||||
|
||||
response_message = ["We've registered your response of \"#{attempt.response}\"."]
|
||||
|
||||
response_count = Attempt.for_question(attempt.question).for_school(recipient.school).with_response.count
|
||||
if response_count > 1
|
||||
response_message << "#{response_count} people have responded to this question so far. To see all responses visit:"
|
||||
answer_count = Attempt.for_question(attempt.question).for_school(recipient.school).with_answer.count
|
||||
if answer_count > 1
|
||||
response_message << "#{answer_count} people have responded to this question so far. To see all responses visit:"
|
||||
else
|
||||
response_message << 'You are the first person to respond to this question. Once more people have responded you will be able to see all responses at:'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,20 +6,32 @@ class Attempt < ApplicationRecord
|
|||
belongs_to :recipient
|
||||
belongs_to :recipient_schedule
|
||||
belongs_to :question
|
||||
belongs_to :student
|
||||
|
||||
after_save :update_school_categories
|
||||
after_commit :update_counts
|
||||
|
||||
scope :for_question, -> (question) { where(question_id: question.id) }
|
||||
scope :for_recipient, -> (recipient) { where(recipient_id: recipient.id) }
|
||||
scope :for_student, -> (student) { where(student_id: student.id) }
|
||||
scope :for_category, -> (category) { joins(:question).merge(Question.for_category(category)) }
|
||||
scope :for_school, -> (school) { joins(:recipient).merge(Recipient.for_school(school)) }
|
||||
scope :with_response, -> { where('answer_index is not null or open_response_id is not null')}
|
||||
scope :with_no_response, -> { where('answer_index is null and open_response_id is null')}
|
||||
scope :with_answer, -> { where('answer_index is not null or open_response_id is not null')}
|
||||
scope :with_no_answer, -> { where('answer_index is null and open_response_id is null')}
|
||||
scope :not_yet_responded, -> { where(responded_at: nil) }
|
||||
|
||||
def messages
|
||||
if student.present?
|
||||
intro = "#{student.name}'s school, "
|
||||
else
|
||||
intro = "Your child's school, "
|
||||
end
|
||||
|
||||
intro += "#{recipient.school.name}, would love your opinion on this question:"
|
||||
|
||||
[
|
||||
question.text,
|
||||
"#{question.option1}: Reply 1\n\r#{question.option2}: Reply 2\n\r#{question.option3}: Reply 3\n\r#{question.option4}: Reply 4\n\r#{question.option5}: Reply 5\n\rReply 'stop' to stop these messages."
|
||||
#question.text,
|
||||
"#{intro}\n\r#{question.text}\n\r#{question.option1}: Reply 1\n\r#{question.option2}: Reply 2\n\r#{question.option3}: Reply 3\n\r#{question.option4}: Reply 4\n\r#{question.option5}: Reply 5\n\rReply 'skip' to skip this question.\n\rReply 'stop' to stop these messages."
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -70,7 +82,7 @@ class Attempt < ApplicationRecord
|
|||
def update_counts
|
||||
recipient.update_attributes(
|
||||
attempts_count: recipient.attempts.count,
|
||||
responses_count: recipient.attempts.with_response.count
|
||||
responses_count: recipient.attempts.with_answer.count
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Question < ApplicationRecord
|
|||
end
|
||||
|
||||
def aggregated_responses_for_school(school)
|
||||
school_responses = attempts.for_school(school).with_response.order(id: :asc)
|
||||
school_responses = attempts.for_school(school).with_answer.order(id: :asc)
|
||||
return unless school_responses.present?
|
||||
|
||||
response_answer_total = school_responses.inject(0) { |total, response| total + response.answer_index }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ class Recipient < ApplicationRecord
|
|||
has_many :recipient_schedules
|
||||
has_many :attempts
|
||||
|
||||
has_many :students
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
scope :for_school, -> (school) { where(school: school) }
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class RecipientList < ApplicationRecord
|
|||
new_ids = recipient_ids.split(/,/)
|
||||
(old_ids - new_ids).each do |deleted_recipient|
|
||||
schedules.each do |schedule|
|
||||
schedule.recipient_schedules.for(deleted_recipient).first.destroy
|
||||
schedule.recipient_schedules.for_recipient(deleted_recipient).first.destroy
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,18 @@ class RecipientSchedule < ApplicationRecord
|
|||
validates :next_attempt_at, presence: true
|
||||
|
||||
scope :ready, -> { where('next_attempt_at <= ?', Time.new) }
|
||||
scope :for, -> (recipient_or_recipient_id) {
|
||||
scope :for_recipient, -> (recipient_or_recipient_id) {
|
||||
id = recipient_or_recipient_id.is_a?(Recipient) ?
|
||||
recipient_or_recipient_id.id :
|
||||
recipient_or_recipient_id
|
||||
where(recipient_id: id)
|
||||
}
|
||||
scope :for_schedule, -> (schedule_or_schedule_id) {
|
||||
id = schedule_or_schedule_id.is_a?(Schedule) ?
|
||||
schedule_or_schedule_id.id :
|
||||
schedule_or_schedule_id
|
||||
where(schedule_id: id)
|
||||
}
|
||||
|
||||
def next_question
|
||||
if queued_question_ids.present?
|
||||
|
|
@ -25,13 +31,79 @@ class RecipientSchedule < ApplicationRecord
|
|||
Question.where(id: next_question_id).first
|
||||
end
|
||||
|
||||
def upcoming_question_id_array
|
||||
upcoming_question_ids.try(:split, /,/) || []
|
||||
end
|
||||
|
||||
def attempted_question_id_array
|
||||
attempted_question_ids.try(:split, /,/) || []
|
||||
end
|
||||
|
||||
def queued_question_id_array
|
||||
queued_question_ids.try(:split, /,/) || []
|
||||
end
|
||||
|
||||
def attempt_question_for_recipient_students(send_message: true, question: next_question)
|
||||
return if recipient.opted_out?
|
||||
return if question.nil?
|
||||
|
||||
if !question.for_recipient_students?
|
||||
return attempt_question(question: question)
|
||||
end
|
||||
|
||||
missing_students = []
|
||||
recipient_attempts = attempts.for_recipient(recipient).for_question(question)
|
||||
recipient.students.each do |student|
|
||||
if recipient_attempts.for_student(student).empty?
|
||||
missing_students << student
|
||||
end
|
||||
end
|
||||
|
||||
attempt = recipient.attempts.create(
|
||||
schedule: schedule,
|
||||
recipient_schedule: self,
|
||||
question: question,
|
||||
student: missing_students.first
|
||||
)
|
||||
|
||||
if send_message && attempt.send_message
|
||||
upcoming = upcoming_question_id_array
|
||||
queued = queued_question_id_array
|
||||
attempted = attempted_question_id_array
|
||||
|
||||
if question.present?
|
||||
question_id = [question.id.to_s]
|
||||
upcoming = upcoming - question_id
|
||||
if missing_students.length > 1
|
||||
queued += question_id
|
||||
else
|
||||
attempted += question_id
|
||||
queued -= question_id
|
||||
end
|
||||
end
|
||||
|
||||
update_attributes(
|
||||
upcoming_question_ids: upcoming.empty? ? nil : upcoming.join(','),
|
||||
attempted_question_ids: attempted.empty? ? nil : attempted.join(','),
|
||||
queued_question_ids: queued.empty? ? nil : queued.join(','),
|
||||
last_attempt_at: attempt.sent_at,
|
||||
next_attempt_at: next_valid_attempt_time
|
||||
)
|
||||
end
|
||||
return attempt
|
||||
end
|
||||
|
||||
def attempt_question(send_message: true, question: next_question)
|
||||
return if recipient.opted_out?
|
||||
unanswered_attempt = recipient.attempts.with_no_response.last
|
||||
unanswered_attempt = recipient.attempts.not_yet_responded.last
|
||||
|
||||
return if question.nil? && unanswered_attempt.nil?
|
||||
|
||||
if unanswered_attempt.nil?
|
||||
if question.for_recipient_students?
|
||||
return attempt_question_for_recipient_students(question: question)
|
||||
end
|
||||
|
||||
attempt = recipient.attempts.create(
|
||||
schedule: schedule,
|
||||
recipient_schedule: self,
|
||||
|
|
@ -40,9 +112,9 @@ class RecipientSchedule < ApplicationRecord
|
|||
end
|
||||
|
||||
if send_message && (unanswered_attempt || attempt).send_message
|
||||
upcoming = upcoming_question_ids.try(:split, /,/) || []
|
||||
queued = queued_question_ids.try(:split, /,/) || []
|
||||
attempted = attempted_question_ids.try(:split, /,/) || []
|
||||
upcoming = upcoming_question_id_array
|
||||
queued = queued_question_id_array
|
||||
attempted = attempted_question_id_array
|
||||
|
||||
if question.present?
|
||||
question_id = [question.id.to_s]
|
||||
|
|
|
|||
3
app/models/student.rb
Normal file
3
app/models/student.rb
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
class Student < ApplicationRecord
|
||||
belongs_to :recipient
|
||||
end
|
||||
|
|
@ -1,29 +1,27 @@
|
|||
[
|
||||
{"category": "6-A-i", "text": "How often do you meet in person with teachers or administrators at your child's school?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "In the past year, how often have you discussed your child's school with other parents from the school?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How involved have you been in activities at your child's school?", "answers": ["Not involved at all", "A little bit involved ", "Somewhat involved", "Quite involved", "Extremely involved"]},
|
||||
{"category": "6-A-i", "text": "In the past year, how often have you helped out at your child's school?", "answers": ["Almost never", "Once or twice ", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to connect with other parents? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to make sure your child's school meets your child's learning needs? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to make choices about your child's schooling? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you that you can motivate your child to try hard in school? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to support your child's learning at home? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you that you can help your child develop good friendships? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "child": true, "text": "How confident are you in your ability to make sure your child's school meets your child's learning needs? ", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to make choices about your child's schooling?", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "child": true, "text": "How confident are you that you can motivate your child to try hard in school?", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "child": true, "text": "How confident are you in your ability to support your child's learning at home?", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "child": true, "text": "How confident are you that you can help your child develop good friendships?", "answers": ["Not confident at all", "A little bit confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
{"category": "6-A-i", "text": "Overall, how good is this school at communicating with parents?", "answers": ["Not good at all", "Slightly good", "Somewhat good", "Quite good", "Extremely good"]},
|
||||
{"category": "6-A-i", "text": "How often do you visit your child's school for an event, activity, or to volunteer? ", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How often do you discuss your child's school with other parents from the school? ", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How often do you visit your child's school for an event, activity, or to volunteer?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How often do you discuss your child's school with other parents from the school?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-i", "text": "How confident are you in your ability to connect with other parents at the school? ", "answers": ["Not confident at all", "Slightly confident", "Somewhat confident", "Quite confident", "Extremely confident"]},
|
||||
|
||||
{"category": "6-A-ii", "text": "How often would you like the school to host social events for families?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-ii", "text": "How often would you like to meet with school staff to talk about helping children succeed academically?", "answers": ["Almost never", "Once or twice a year", "Every few months", "Monthly", "Weekly or more"]},
|
||||
{"category": "6-A-ii", "text": "How much would you enjoy spending an hour in your child’s classroom?", "answers": ["Would not enjoy that at all", "Would enjoy that a little bit", "Would enjoy that somewhat", "Would enjoy that quite a bit", "Would enjoy that a tremendous amount"]},
|
||||
{"category": "6-A-ii", "child": true, "text": "How much would you enjoy spending an hour in your child’s classroom?", "answers": ["Would not enjoy that at all", "Would enjoy that a little bit", "Would enjoy that somewhat", "Would enjoy that quite a bit", "Would enjoy that a tremendous amount"]},
|
||||
{"category": "6-A-ii", "text": "How much would you like to receive text messages from the school about upcoming events?", "answers": ["Would not like that at all", "Would like that a little bit", "Would like that somewhat", "Would like that quite a bit", "Would like that a tremendous amount"]},
|
||||
{"category": "6-A-ii", "text": "How much would you like to receive messages from the school translated into another language?", "answers": ["Would not like that at all", "Would like that a little bit", "Would like that somewhat", "Would like that quite a bit", "Would like that a tremendous amount"]},
|
||||
|
||||
{"category": "6-A-iii", "text": "How would you describe your sense of belonging in the school community? ", "answers": ["Very weak", "Weak", "Neutral", "Strong", "Very strong"]},
|
||||
{"category": "6-A-iii", "text": "Overall, how good is this school?", "answers": ["Not good at all", "Slightly good", "Somewhat good", "Quite good", "Extremely good"]},
|
||||
{"category": "6-A-iii", "text": "Overall, how welcome do you feel at this school?", "answers": ["Not welcome at all", "Slightly welcome", "Somewhat welcome", "Quite welcome", "Extremely welcome"]},
|
||||
{"category": "6-A-iii", "text": "Overall, how well do you get along well with your child’s teacher(s)?", "answers": ["Do not get along well at all", "Get along a little bit", "Get along somewhat", "Get along quite well", "Get along extremely well"]},
|
||||
{"category": "6-A-iii", "text": "Overall, how much do the teachers at this school care about your child?", "answers": ["Do not care about my child at all", "Care about my child a little bit", "Care about my child somewhat", "Care about my child quite a bit", "Care about my child a tremendous amount"]},
|
||||
{"category": "6-A-iii", "child": true, "text": "Overall, how well do you get along well with your child’s teacher(s)?", "answers": ["Do not get along well at all", "Get along a little bit", "Get along somewhat", "Get along quite well", "Get along extremely well"]},
|
||||
{"category": "6-A-iii", "child": true, "text": "Overall, how much do the teachers at this school care about your child?", "answers": ["Do not care about my child at all", "Care about my child a little bit", "Care about my child somewhat", "Care about my child quite a bit", "Care about my child a tremendous amount"]},
|
||||
{"category": "6-A-iii", "text": "Overall, how much do people at this school care about your family?", "answers": ["Don’t care at all", "Care a little bit", "Care about my family somewhat ", "Care about my family quite a bit", "Care about my family a tremendous amount"]}
|
||||
]
|
||||
|
|
|
|||
18
db/migrate/20170418173141_create_students.rb
Normal file
18
db/migrate/20170418173141_create_students.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
class CreateStudents < ActiveRecord::Migration[5.0]
|
||||
def change
|
||||
create_table :students do |t|
|
||||
t.string :name
|
||||
t.string :teacher
|
||||
t.date :birthdate
|
||||
t.string :gender
|
||||
t.string :age
|
||||
t.string :ethnicity
|
||||
t.integer :recipient_id
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_column :questions, :for_recipient_students, :boolean, default: false
|
||||
add_column :attempts, :student_id, :integer
|
||||
end
|
||||
end
|
||||
22
db/schema.rb
22
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20170417203127) do
|
||||
ActiveRecord::Schema.define(version: 20170418173141) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
|
@ -29,6 +29,7 @@ ActiveRecord::Schema.define(version: 20170417203127) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.text "twilio_details"
|
||||
t.string "twilio_sid"
|
||||
t.integer "student_id"
|
||||
t.index ["twilio_sid"], name: "index_attempts_on_twilio_sid", using: :btree
|
||||
end
|
||||
|
||||
|
|
@ -67,9 +68,10 @@ ActiveRecord::Schema.define(version: 20170417203127) do
|
|||
t.string "option4"
|
||||
t.string "option5"
|
||||
t.integer "category_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "target_group", default: 0
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "target_group", default: 0
|
||||
t.boolean "for_recipient_students", default: false
|
||||
end
|
||||
|
||||
create_table "recipient_lists", force: :cascade do |t|
|
||||
|
|
@ -155,6 +157,18 @@ ActiveRecord::Schema.define(version: 20170417203127) do
|
|||
t.index ["slug"], name: "index_schools_on_slug", unique: true, using: :btree
|
||||
end
|
||||
|
||||
create_table "students", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "teacher"
|
||||
t.date "birthdate"
|
||||
t.string "gender"
|
||||
t.string "age"
|
||||
t.string "ethnicity"
|
||||
t.integer "recipient_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "user_schools", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "school_id"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@ recipients = [
|
|||
school.recipients.create(name: 'Lynisse Patin', phone: '19176566892'),
|
||||
school.recipients.create(name: 'Khemenec Patin', phone: '(347) 534-6437'),
|
||||
]
|
||||
|
||||
recipients[0].students.create(name: 'Abigail')
|
||||
recipients[0].students.create(name: 'Clara')
|
||||
|
||||
recipients[3].students.creaet(name: 'Zara')
|
||||
recipients[3].students.creaet(name: 'Cole')
|
||||
recipients[4].students.creaet(name: 'Zara')
|
||||
recipients[4].students.creaet(name: 'Cole')
|
||||
|
||||
recipient_list = school.recipient_lists.create(name: 'Pilot Parent Test', recipient_id_array: recipients.map(&:id))
|
||||
|
||||
user.user_schools.create(school: school)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ namespace :data do
|
|||
option2: question['answers'][1],
|
||||
option3: question['answers'][2],
|
||||
option4: question['answers'][3],
|
||||
option5: question['answers'][4]
|
||||
option5: question['answers'][4],
|
||||
for_recipient_students: question['child'].present?
|
||||
)
|
||||
else
|
||||
variations.each do |variation|
|
||||
|
|
@ -112,7 +113,8 @@ namespace :data do
|
|||
option2: question['answers'][1],
|
||||
option3: question['answers'][2],
|
||||
option4: question['answers'][3],
|
||||
option5: question['answers'][4]
|
||||
option5: question['answers'][4],
|
||||
for_recipient_students: question['child'].present?
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ RSpec.describe AttemptsController, type: :controller do
|
|||
end
|
||||
|
||||
it 'creates the first attempt with response for the question' do
|
||||
expect(attempt.question.attempts.for_school(school).with_response.count).to eq(1)
|
||||
expect(attempt.question.attempts.for_school(school).with_answer.count).to eq(1)
|
||||
end
|
||||
|
||||
it "updates the last attempt by recipient phone number" do
|
||||
|
|
@ -86,7 +86,7 @@ RSpec.describe AttemptsController, type: :controller do
|
|||
end
|
||||
|
||||
it 'updates the second attempt with response for the school' do
|
||||
expect(attempt.question.attempts.for_school(school).with_response.count).to eq(2)
|
||||
expect(attempt.question.attempts.for_school(school).with_answer.count).to eq(2)
|
||||
end
|
||||
|
||||
it "updates the attempt from the second recipient" do
|
||||
|
|
@ -120,5 +120,30 @@ RSpec.describe AttemptsController, type: :controller do
|
|||
expect(response.body).to eq('Thank you, you have been opted out of these messages and will no longer receive them.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with skip params' do
|
||||
let(:twilio_skip_attributes) {
|
||||
{'MessageSid' => 'ewuefhwieuhfweiuhfewiuhf','AccountSid' => 'wefiuwhefuwehfuwefinwefw','MessagingServiceSid' => 'efwneufhwuefhweiufhiuewhf','From' => '+0000000000','To' => '2223334444','Body' => 'SkIP','NumMedia' => '0'}
|
||||
}
|
||||
|
||||
it "updates the last attempt by recipient phone number" do
|
||||
post :twilio, params: twilio_skip_attributes
|
||||
attempt.reload
|
||||
expect(attempt.answer_index).to be_nil
|
||||
expect(attempt.responded_at).to be_present
|
||||
expect(attempt.twilio_details).to eq(twilio_skip_attributes.with_indifferent_access.to_yaml)
|
||||
expect(attempt.recipient).to_not be_opted_out
|
||||
|
||||
school_attempts = attempt.question.attempts.for_school(school)
|
||||
expect(school_attempts.with_answer.count).to eq(0)
|
||||
expect(school_attempts.with_no_answer.count).to eq(3)
|
||||
expect(school_attempts.not_yet_responded.count).to eq(2)
|
||||
end
|
||||
|
||||
it "sends back a message" do
|
||||
post :twilio, params: twilio_skip_attributes
|
||||
expect(response.body).to eq('Thank you, this question has been skipped.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ describe "survey:attempt_questions" do
|
|||
describe 'Second Attempts' do
|
||||
before :each do
|
||||
recipients.each do |recipient|
|
||||
recipient_schedule = schedule.recipient_schedules.for(recipient).first
|
||||
recipient_schedule = schedule.recipient_schedules.for_recipient(recipient).first
|
||||
recipient_schedule.attempt_question
|
||||
end
|
||||
end
|
||||
|
|
@ -161,7 +161,7 @@ describe "survey:attempt_questions" do
|
|||
|
||||
Timecop.freeze(now + 7)
|
||||
recipients.each do |recipient|
|
||||
recipient_schedule = schedule.recipient_schedules.for(recipient).first
|
||||
recipient_schedule = schedule.recipient_schedules.for_recipient(recipient).first
|
||||
recipient_schedule.attempt_question
|
||||
end
|
||||
|
||||
|
|
@ -185,13 +185,150 @@ describe "survey:attempt_questions" do
|
|||
end
|
||||
|
||||
it 'should not send anything to anyone else' do
|
||||
expect(FakeSMS.messages.length).to eq(@existing_message_count + 2)
|
||||
expect(FakeSMS.messages.length).to eq(@existing_message_count + 1)
|
||||
expect(recipients[0].attempts.count).to eq(1)
|
||||
expect(recipients[1].attempts.count).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Multiple Students In A Family' do
|
||||
|
||||
before :each do
|
||||
3.times do |i|
|
||||
recipients[1].students.create(name: "Student#{i}")
|
||||
end
|
||||
end
|
||||
|
||||
let(:students_recipient) { recipients[1] }
|
||||
let(:students_recipient_schedule) {
|
||||
students_recipient.recipient_schedules.for_schedule(schedule).first
|
||||
}
|
||||
|
||||
describe 'With A FOR_CHILD Question Is Asked' do
|
||||
let!(:date) { ActiveSupport::TimeZone["UTC"].parse(now.strftime("%Y-%m-%dT20:00:00%z")) }
|
||||
|
||||
before :each do
|
||||
questions.first.update_attributes(for_recipient_students: true)
|
||||
Timecop.freeze(date) { subject.invoke }
|
||||
end
|
||||
|
||||
it 'should create one attempt per recipient regardless of students' do
|
||||
expect(FakeSMS.messages.length).to eq(3)
|
||||
recipients.each do |recipient|
|
||||
expect(recipient.attempts.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should store queued questions when an attempt is made on first student' do
|
||||
expect(students_recipient_schedule.queued_question_ids).to be_present
|
||||
queued_question_ids = students_recipient_schedule.queued_question_ids.split(/,/)
|
||||
expect(queued_question_ids.length).to eq(1)
|
||||
expect(queued_question_ids.first).to eq("#{questions[0].id}")
|
||||
end
|
||||
|
||||
it 'should set the next_attempt_at to now when attempt is made on first student' do
|
||||
students_recipient.attempts.last.save_response(answer_index: 3)
|
||||
expect(students_recipient_schedule.reload.next_attempt_at).to eq(Time.new)
|
||||
end
|
||||
|
||||
it 'should set the next_attempt_at in the future when an attempts are made on each student' do
|
||||
students_recipient.attempts.last.save_response(answer_index: 3)
|
||||
expect{students_recipient_schedule.attempt_question}.to change{students_recipient.attempts.count}.by(1)
|
||||
expect(students_recipient_schedule.reload.queued_question_ids).to be_present
|
||||
|
||||
attempt = students_recipient.attempts.last
|
||||
expect(attempt.student).to eq(students_recipient.students[1])
|
||||
|
||||
Timecop.freeze(date + 1.day)
|
||||
attempt.save_response(answer_index: 4)
|
||||
expect(students_recipient_schedule.reload.next_attempt_at).to eq(date + 1.day)
|
||||
|
||||
expect{students_recipient_schedule.attempt_question}.to change{students_recipient.attempts.count}.by(1)
|
||||
expect(students_recipient_schedule.reload.queued_question_ids).to be_nil
|
||||
expect(students_recipient_schedule.reload.next_attempt_at).to_not eq(date + (60 * 60 * schedule.frequency_hours))
|
||||
|
||||
attempt = students_recipient.attempts.last
|
||||
expect(attempt.student).to eq(students_recipient.students[2])
|
||||
|
||||
Timecop.freeze(date + 2.days)
|
||||
attempt.save_response(answer_index: 2)
|
||||
expect(students_recipient_schedule.reload.next_attempt_at).to_not eq(date + 2.days)
|
||||
end
|
||||
|
||||
it 'should mention the students name in the text' do
|
||||
expect(FakeSMS.messages[1].body).to match(/Student0's school, School, would love your opinion on this question/)
|
||||
end
|
||||
|
||||
it 'should not mention the students name in the text if the recipient has no student specified' do
|
||||
expect(FakeSMS.messages[0].body).to match(/Your child's school, School, would love your opinion on this question/)
|
||||
end
|
||||
|
||||
it 'resends the question about the same student if not responded to' do
|
||||
message_count = FakeSMS.messages.length
|
||||
expect{students_recipient_schedule.attempt_question}.to change{students_recipient.attempts.count}.by(0)
|
||||
expect(FakeSMS.messages.length).to eq(message_count + 1)
|
||||
expect(FakeSMS.messages.last.body).to match(/Student0's school, School/)
|
||||
expect(FakeSMS.messages.last.body).to match(questions.first.text)
|
||||
end
|
||||
|
||||
it 'doesnt store any queued_question_ids when no students are present' do
|
||||
recipient_schedule = recipients[0].recipient_schedules.for_schedule(schedule).first
|
||||
expect(recipient_schedule.queued_question_ids).to be_nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'With A General Question Is Asked' do
|
||||
before :each do
|
||||
subject.invoke
|
||||
end
|
||||
|
||||
it 'should not queue up an questions regardless of how many students there are' do
|
||||
expect(students_recipient_schedule.queued_question_ids).to be_nil
|
||||
end
|
||||
|
||||
it 'should not mention the students name in the text' do
|
||||
FakeSMS.messages.each do |message|
|
||||
expect(message.body).to match(/Your child's school, School, would love your opinion on this question/)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe 'One Student In A Family' do
|
||||
|
||||
before :each do
|
||||
recipients[1].students.create(name: "Only Student")
|
||||
end
|
||||
|
||||
let(:students_recipient) { recipients[1] }
|
||||
let(:students_recipient_schedule) {
|
||||
students_recipient.recipient_schedules.for_schedule(schedule).first
|
||||
}
|
||||
|
||||
describe 'With A FOR_CHILD Question Is Asked' do
|
||||
let!(:date) { ActiveSupport::TimeZone["UTC"].parse(now.strftime("%Y-%m-%dT20:00:00%z")) }
|
||||
|
||||
before :each do
|
||||
questions.first.update_attributes(for_recipient_students: true)
|
||||
Timecop.freeze(date) { subject.invoke }
|
||||
end
|
||||
|
||||
it 'should create one attempt per recipient regardless of students' do
|
||||
expect(FakeSMS.messages.length).to eq(3)
|
||||
recipients.each do |recipient|
|
||||
expect(recipient.attempts.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'doesnt store any queued_question_ids' do
|
||||
expect(students_recipient_schedule.queued_question_ids).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Opted Out Recipient' do
|
||||
|
||||
before :each do
|
||||
|
|
|
|||
|
|
@ -92,13 +92,13 @@ RSpec.describe Attempt, type: :model do
|
|||
end
|
||||
|
||||
it 'should contact the Twilio API' do
|
||||
expect(FakeSMS.messages.length).to eq(2)
|
||||
|
||||
expect(FakeSMS.messages.first.to).to eq('111111111')
|
||||
expect(FakeSMS.messages.first.body).to eq("Question 0:1")
|
||||
expect(FakeSMS.messages.length).to eq(1)
|
||||
|
||||
# expect(FakeSMS.messages.first.to).to eq('111111111')
|
||||
# expect(FakeSMS.messages.first.body).to eq("Question 0:1")
|
||||
|
||||
expect(FakeSMS.messages.last.to).to eq('111111111')
|
||||
expect(FakeSMS.messages.last.body).to eq("Option 0:1 A: Reply 1\n\rOption 0:1 B: Reply 2\n\rOption 0:1 C: Reply 3\n\rOption 0:1 D: Reply 4\n\rOption 0:1 E: Reply 5\n\rReply 'stop' to stop these messages.")
|
||||
expect(FakeSMS.messages.last.body).to eq("Your child's school, School, would love your opinion on this question:\n\rQuestion 0:1\n\rOption 0:1 A: Reply 1\n\rOption 0:1 B: Reply 2\n\rOption 0:1 C: Reply 3\n\rOption 0:1 D: Reply 4\n\rOption 0:1 E: Reply 5\n\rReply 'skip' to skip this question.\n\rReply 'stop' to stop these messages.")
|
||||
end
|
||||
|
||||
it 'should update sent_at' do
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ RSpec.describe RecipientSchedule, type: :model do
|
|||
frequency_hours: 24
|
||||
)
|
||||
end
|
||||
let!(:recipient_schedule) { schedule.recipient_schedules.for(recipient).first }
|
||||
let!(:recipient_schedule) { schedule.recipient_schedules.for_recipient(recipient).first }
|
||||
|
||||
let!(:not_ready_recipient_schedule) do
|
||||
RecipientSchedule.create!(
|
||||
|
|
@ -91,6 +91,11 @@ RSpec.describe RecipientSchedule, type: :model do
|
|||
end
|
||||
|
||||
describe 'with an opted in recipient' do
|
||||
before :each do
|
||||
date = ActiveSupport::TimeZone["UTC"].parse('2017-04-20T20:00:00')
|
||||
Timecop.freeze(date)
|
||||
end
|
||||
|
||||
let!(:attempt) { recipient_schedule.attempt_question }
|
||||
|
||||
it 'should make an attempt to ask the next question' do
|
||||
|
|
@ -121,7 +126,7 @@ RSpec.describe RecipientSchedule, type: :model do
|
|||
date += 1.day
|
||||
date += 1.day if date.on_weekend?
|
||||
|
||||
expect(recipient_schedule.next_attempt_at).to eq(date.to_time)
|
||||
expect(recipient_schedule.reload.next_attempt_at).to eq(date.to_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
5
spec/models/student_spec.rb
Normal file
5
spec/models/student_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Student, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue