Merge pull request #1 from jaredcosulich/recipient_students

Recipient students
pull/1/head
jaredcosulich 9 years ago committed by GitHub
commit 0c2aa09ebc

@ -14,15 +14,20 @@ class AttemptsController < ApplicationController
end end
attempt.save_response( 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 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_message = ["We've registered your response of \"#{attempt.response}\"."]
response_count = Attempt.for_question(attempt.question).for_school(recipient.school).with_response.count answer_count = Attempt.for_question(attempt.question).for_school(recipient.school).with_answer.count
if response_count > 1 if answer_count > 1
response_message << "#{response_count} people have responded to this question so far. To see all responses visit:" response_message << "#{answer_count} people have responded to this question so far. To see all responses visit:"
else 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:' 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 end

@ -6,20 +6,32 @@ class Attempt < ApplicationRecord
belongs_to :recipient belongs_to :recipient
belongs_to :recipient_schedule belongs_to :recipient_schedule
belongs_to :question belongs_to :question
belongs_to :student
after_save :update_school_categories after_save :update_school_categories
after_commit :update_counts after_commit :update_counts
scope :for_question, -> (question) { where(question_id: question.id) } 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_category, -> (category) { joins(:question).merge(Question.for_category(category)) }
scope :for_school, -> (school) { joins(:recipient).merge(Recipient.for_school(school)) } 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_answer, -> { 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_no_answer, -> { where('answer_index is null and open_response_id is null')}
scope :not_yet_responded, -> { where(responded_at: nil) }
def messages 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.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." "#{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 end
@ -70,7 +82,7 @@ class Attempt < ApplicationRecord
def update_counts def update_counts
recipient.update_attributes( recipient.update_attributes(
attempts_count: recipient.attempts.count, attempts_count: recipient.attempts.count,
responses_count: recipient.attempts.with_response.count responses_count: recipient.attempts.with_answer.count
) )
end end

@ -37,7 +37,7 @@ class Question < ApplicationRecord
end end
def aggregated_responses_for_school(school) 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? return unless school_responses.present?
response_answer_total = school_responses.inject(0) { |total, response| total + response.answer_index } 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 :recipient_schedules
has_many :attempts has_many :attempts
has_many :students
validates :name, presence: true validates :name, presence: true
scope :for_school, -> (school) { where(school: school) } scope :for_school, -> (school) { where(school: school) }

@ -33,7 +33,7 @@ class RecipientList < ApplicationRecord
new_ids = recipient_ids.split(/,/) new_ids = recipient_ids.split(/,/)
(old_ids - new_ids).each do |deleted_recipient| (old_ids - new_ids).each do |deleted_recipient|
schedules.each do |schedule| schedules.each do |schedule|
schedule.recipient_schedules.for(deleted_recipient).first.destroy schedule.recipient_schedules.for_recipient(deleted_recipient).first.destroy
end end
end end

@ -9,12 +9,18 @@ class RecipientSchedule < ApplicationRecord
validates :next_attempt_at, presence: true validates :next_attempt_at, presence: true
scope :ready, -> { where('next_attempt_at <= ?', Time.new) } 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) ? id = recipient_or_recipient_id.is_a?(Recipient) ?
recipient_or_recipient_id.id : recipient_or_recipient_id.id :
recipient_or_recipient_id recipient_or_recipient_id
where(recipient_id: 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 def next_question
if queued_question_ids.present? if queued_question_ids.present?
@ -25,13 +31,79 @@ class RecipientSchedule < ApplicationRecord
Question.where(id: next_question_id).first Question.where(id: next_question_id).first
end 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) def attempt_question(send_message: true, question: next_question)
return if recipient.opted_out? 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? return if question.nil? && unanswered_attempt.nil?
if 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( attempt = recipient.attempts.create(
schedule: schedule, schedule: schedule,
recipient_schedule: self, recipient_schedule: self,
@ -40,9 +112,9 @@ class RecipientSchedule < ApplicationRecord
end end
if send_message && (unanswered_attempt || attempt).send_message if send_message && (unanswered_attempt || attempt).send_message
upcoming = upcoming_question_ids.try(:split, /,/) || [] upcoming = upcoming_question_id_array
queued = queued_question_ids.try(:split, /,/) || [] queued = queued_question_id_array
attempted = attempted_question_ids.try(:split, /,/) || [] attempted = attempted_question_id_array
if question.present? if question.present?
question_id = [question.id.to_s] question_id = [question.id.to_s]

@ -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": "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": "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": "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", "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 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 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", "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", "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": "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": "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 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 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-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 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 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 childs 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 childs 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 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-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": "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 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 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 childs 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 well do you get along well with your childs 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 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": ["Dont 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"]} {"category": "6-A-iii", "text": "Overall, how much do people at this school care about your family?", "answers": ["Dont 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"]}
] ]

@ -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

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -29,6 +29,7 @@ ActiveRecord::Schema.define(version: 20170417203127) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.text "twilio_details" t.text "twilio_details"
t.string "twilio_sid" t.string "twilio_sid"
t.integer "student_id"
t.index ["twilio_sid"], name: "index_attempts_on_twilio_sid", using: :btree t.index ["twilio_sid"], name: "index_attempts_on_twilio_sid", using: :btree
end end
@ -67,9 +68,10 @@ ActiveRecord::Schema.define(version: 20170417203127) do
t.string "option4" t.string "option4"
t.string "option5" t.string "option5"
t.integer "category_id" t.integer "category_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "target_group", default: 0 t.integer "target_group", default: 0
t.boolean "for_recipient_students", default: false
end end
create_table "recipient_lists", force: :cascade do |t| 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 t.index ["slug"], name: "index_schools_on_slug", unique: true, using: :btree
end 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| create_table "user_schools", force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.integer "school_id" t.integer "school_id"

@ -21,6 +21,15 @@ recipients = [
school.recipients.create(name: 'Lynisse Patin', phone: '19176566892'), school.recipients.create(name: 'Lynisse Patin', phone: '19176566892'),
school.recipients.create(name: 'Khemenec Patin', phone: '(347) 534-6437'), 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)) recipient_list = school.recipient_lists.create(name: 'Pilot Parent Test', recipient_id_array: recipients.map(&:id))
user.user_schools.create(school: school) user.user_schools.create(school: school)

@ -102,7 +102,8 @@ namespace :data do
option2: question['answers'][1], option2: question['answers'][1],
option3: question['answers'][2], option3: question['answers'][2],
option4: question['answers'][3], option4: question['answers'][3],
option5: question['answers'][4] option5: question['answers'][4],
for_recipient_students: question['child'].present?
) )
else else
variations.each do |variation| variations.each do |variation|
@ -112,7 +113,8 @@ namespace :data do
option2: question['answers'][1], option2: question['answers'][1],
option3: question['answers'][2], option3: question['answers'][2],
option4: question['answers'][3], option4: question['answers'][3],
option5: question['answers'][4] option5: question['answers'][4],
for_recipient_students: question['child'].present?
) )
end end
end end

@ -58,7 +58,7 @@ RSpec.describe AttemptsController, type: :controller do
end end
it 'creates the first attempt with response for the question' do 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 end
it "updates the last attempt by recipient phone number" do it "updates the last attempt by recipient phone number" do
@ -86,7 +86,7 @@ RSpec.describe AttemptsController, type: :controller do
end end
it 'updates the second attempt with response for the school' do 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 end
it "updates the attempt from the second recipient" do 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.') expect(response.body).to eq('Thank you, you have been opted out of these messages and will no longer receive them.')
end end
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
end end

@ -105,7 +105,7 @@ describe "survey:attempt_questions" do
describe 'Second Attempts' do describe 'Second Attempts' do
before :each do before :each do
recipients.each do |recipient| 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 recipient_schedule.attempt_question
end end
end end
@ -161,7 +161,7 @@ describe "survey:attempt_questions" do
Timecop.freeze(now + 7) Timecop.freeze(now + 7)
recipients.each do |recipient| 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 recipient_schedule.attempt_question
end end
@ -185,13 +185,150 @@ describe "survey:attempt_questions" do
end end
it 'should not send anything to anyone else' do 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[0].attempts.count).to eq(1)
expect(recipients[1].attempts.count).to eq(2) expect(recipients[1].attempts.count).to eq(2)
end end
end 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 describe 'Opted Out Recipient' do
before :each do before :each do

@ -92,13 +92,13 @@ RSpec.describe Attempt, type: :model do
end end
it 'should contact the Twilio API' do it 'should contact the Twilio API' do
expect(FakeSMS.messages.length).to eq(2) expect(FakeSMS.messages.length).to eq(1)
expect(FakeSMS.messages.first.to).to eq('111111111') # expect(FakeSMS.messages.first.to).to eq('111111111')
expect(FakeSMS.messages.first.body).to eq("Question 0:1") # expect(FakeSMS.messages.first.body).to eq("Question 0:1")
expect(FakeSMS.messages.last.to).to eq('111111111') 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 end
it 'should update sent_at' do it 'should update sent_at' do

@ -24,7 +24,7 @@ RSpec.describe RecipientSchedule, type: :model do
frequency_hours: 24 frequency_hours: 24
) )
end 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 let!(:not_ready_recipient_schedule) do
RecipientSchedule.create!( RecipientSchedule.create!(
@ -91,6 +91,11 @@ RSpec.describe RecipientSchedule, type: :model do
end end
describe 'with an opted in recipient' do 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 } let!(:attempt) { recipient_schedule.attempt_question }
it 'should make an attempt to ask the next question' do 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
date += 1.day if date.on_weekend? 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 end
end end

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Student, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Loading…
Cancel
Save