diff --git a/.gitignore b/.gitignore index bab620de..724057be 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ # Ignore Byebug command history file. .byebug_history +config/local_env.yml diff --git a/app/models/attempt.rb b/app/models/attempt.rb index 676832d3..2a6857b6 100644 --- a/app/models/attempt.rb +++ b/app/models/attempt.rb @@ -7,7 +7,6 @@ class Attempt < ApplicationRecord belongs_to :recipient_schedule belongs_to :question - def send_message twilio_number = ENV['TWILIO_NUMBER'] client = Twilio::REST::Client.new ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'] diff --git a/app/models/recipient_list.rb b/app/models/recipient_list.rb index c82af6ef..08faea8f 100644 --- a/app/models/recipient_list.rb +++ b/app/models/recipient_list.rb @@ -1,6 +1,7 @@ class RecipientList < ApplicationRecord belongs_to :school + validates_associated :school validates :name, presence: true validates :recipient_ids, presence: true diff --git a/app/models/recipient_schedule.rb b/app/models/recipient_schedule.rb index ffff5d24..35da6c0f 100644 --- a/app/models/recipient_schedule.rb +++ b/app/models/recipient_schedule.rb @@ -4,7 +4,11 @@ class RecipientSchedule < ApplicationRecord belongs_to :schedule has_many :attempts + validates_associated :recipient + validates_associated :schedule + scope :ready, -> { where('next_attempt_at <= ?', Time.new) } + scope :for, -> (recipient) { where(recipient_id: recipient.id) } def next_question upcoming = upcoming_question_ids.split(/,/) @@ -19,8 +23,9 @@ class RecipientSchedule < ApplicationRecord ) if attempt.send_message + return if upcoming_question_ids.blank? upcoming = upcoming_question_ids.split(/,/)[1..-1].join(',') - attempted = (attempted_question_ids.split(/,/) + [question.id]).join(',') + attempted = ((attempted_question_ids.try(:split, /,/) || []) + [question.id]).join(',') update_attributes( upcoming_question_ids: upcoming, attempted_question_ids: attempted, diff --git a/app/models/schedule.rb b/app/models/schedule.rb index 6053e66e..27665f3c 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -8,6 +8,22 @@ class Schedule < ApplicationRecord validates :recipient_list, presence: true validates :question_list, presence: true + after_create :create_recipient_schedules + scope :active, -> { where(active: true).where("start_date <= ? and end_date > ?", Date.today, Date.today) } + private + + def create_recipient_schedules + recipient_list.recipients.each do |recipient| + question_ids = question_list.question_ids.split(/,/) + question_ids = question_ids.shuffle if random + recipient_schedules.create( + recipient: recipient, + upcoming_question_ids: question_ids.join(','), + next_attempt_at: Time.new + ) + end + end + end diff --git a/app/views/question_lists/_form.html.haml b/app/views/question_lists/_form.html.haml index 68f7c2a6..223a2670 100644 --- a/app/views/question_lists/_form.html.haml +++ b/app/views/question_lists/_form.html.haml @@ -7,10 +7,10 @@ %ul - question_list.errors.full_messages.each do |message| %li= message - .field + .form-group = f.label :name = f.text_field :name, class: 'form-control' - .field + .form-group = f.label :description = f.text_area :description, class: 'form-control' .form-group diff --git a/config/environments/development.rb b/config/environments/development.rb index 75360736..25f44cc7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,12 @@ Rails.application.configure do + + config.before_configuration do + env_file = File.join(Rails.root, 'config', 'local_env.yml') + YAML.load(File.open(env_file)).each do |key, value| + ENV[key.to_s] = value + end if File.exists?(env_file) + end + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on diff --git a/spec/controllers/schedules_controller_spec.rb b/spec/controllers/schedules_controller_spec.rb index 153d2b82..9aeb1134 100644 --- a/spec/controllers/schedules_controller_spec.rb +++ b/spec/controllers/schedules_controller_spec.rb @@ -20,9 +20,17 @@ require 'rails_helper' RSpec.describe SchedulesController, type: :controller do - let(:school) { School.create!(name: 'School') } - let(:recipient_list) { RecipientList.create!(name: 'Parents', recipient_id_array: [1, 2, 3]) } - let(:question_list) { QuestionList.create!(name: 'Parents Questions', question_id_array: [1, 2, 3]) } + let!(:school) { School.create!(name: 'School') } + + let!(:recipients) { create_recipients(school, 3) } + let!(:recipient_list) do + school.recipient_lists.create!(name: 'Parents', recipient_ids: recipients.map(&:id).join(',')) + end + + let!(:questions) { create_questions(3) } + let!(:question_list) do + QuestionList.create!(name: 'Parent Questions', question_ids: questions.map(&:id).join(',')) + end # This should return the minimal set of attributes required to create a valid # Schedule. As you add validations to Schedule, be sure to diff --git a/spec/lib/tasks/survey_rake_spec.rb b/spec/lib/tasks/survey_rake_spec.rb index cf1c49e9..7452f955 100644 --- a/spec/lib/tasks/survey_rake_spec.rb +++ b/spec/lib/tasks/survey_rake_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe "survey:attempt_qustions" do include_context "rake" - let(:ready_recipient_schedule) { double('ready recipient schedule', attempt_question: nil) } + let(:ready_recipient_schedule) { double('ready recipient schedule', attempt_question: nil) } let(:recipient_schedules) { double("recipient schedules", ready: [ready_recipient_schedule]) } let(:active_schedule) { double("active schedule", recipient_schedules: recipient_schedules) } diff --git a/spec/models/attempt_spec.rb b/spec/models/attempt_spec.rb index eabbf712..2524c2e8 100644 --- a/spec/models/attempt_spec.rb +++ b/spec/models/attempt_spec.rb @@ -2,11 +2,17 @@ require 'rails_helper' RSpec.describe Attempt, type: :model do - let(:question) { Question.create!(text: 'What is the question?', option1: 'A', option2: 'B', option3: 'C', option4: 'D', option5: 'E')} - let(:question_list) { QuestionList.create(name: 'Parent Questions', question_ids: "#{question.id},2,3")} + let!(:school) { School.create!(name: 'School') } - let(:recipient) { Recipient.create!(name: 'Parent', phone: '1112223333') } - let(:recipient_list) { RecipientList.create(name: 'Parent List', recipient_ids: recipient.id.to_s)} + let!(:recipient) { create_recipients(school, 1).first } + let!(:recipient_list) do + school.recipient_lists.create!(name: 'Parents', recipient_ids: "#{recipient.id}") + end + + let!(:question) { create_questions(1).first } + let!(:question_list) do + QuestionList.create!(name: 'Parent Questions', question_ids: "#{question.id}") + end let(:schedule) { Schedule.create!(name: 'Parent Schedule', recipient_list_id: recipient_list.id, question_list: question_list) } diff --git a/spec/models/recipient_list_spec.rb b/spec/models/recipient_list_spec.rb index 6396d2f3..b92a79ce 100644 --- a/spec/models/recipient_list_spec.rb +++ b/spec/models/recipient_list_spec.rb @@ -1,8 +1,7 @@ require 'rails_helper' -describe Recipient do +describe RecipientList do describe "Save" do - it "should convert the recipient_id_array into the recipient_ids attribute" do recipient_list = RecipientList.create(name: 'Name', recipient_id_array: ['', '1', '2', '3']) expect(recipient_list).to be_a(RecipientList) @@ -12,6 +11,11 @@ describe Recipient do recipient_list.update(recipient_id_array: ['3', '', '4', '5', '6']) expect(recipient_list.reload.recipient_ids).to eq('3,4,5,6') end + end + + describe "when edited" do + it 'should delete recipient_schedules if a recipient is removed' + it 'should create recipient_schedules if a recipient is added' end end diff --git a/spec/models/recipient_schedule_spec.rb b/spec/models/recipient_schedule_spec.rb index 766f0f26..4b22efef 100644 --- a/spec/models/recipient_schedule_spec.rb +++ b/spec/models/recipient_schedule_spec.rb @@ -2,37 +2,34 @@ require 'rails_helper' RSpec.describe RecipientSchedule, type: :model do - let(:question) { Question.create!(text: 'What is the question?', option1: 'A', option2: 'B', option3: 'C', option4: 'D', option5: 'E')} - let(:question_list) { QuestionList.create(name: 'Parent Questions', question_ids: "#{question.id},2,3")} + let!(:school) { School.create!(name: 'School') } - let(:recipient) { Recipient.create!(name: 'Parent', phone: '1112223333') } - let(:recipient_list) { RecipientList.create(name: 'Parent List', recipient_ids: recipient.id.to_s)} + let!(:recipient) { create_recipients(school, 1).first } + let!(:recipient_list) do + school.recipient_lists.create!(name: 'Parents', recipient_ids: "#{recipient.id}") + end + + let!(:questions) { create_questions(3) } + let!(:question_list) do + QuestionList.create!(name: 'Parent Questions', question_ids: questions.map(&:id).join(',')) + end - let(:schedule) do + let!(:schedule) do Schedule.create!( name: 'Parent Schedule', recipient_list_id: recipient_list.id, question_list: question_list, + random: false, frequency_hours: 24 * 7 ) end - - let!(:recipient_schedule) do - RecipientSchedule.create!( - recipient: recipient, - schedule: schedule, - upcoming_question_ids: "#{question.id},3", - attempted_question_ids: '2', - last_attempt_at: 2.weeks.ago, - next_attempt_at: 2.weeks.ago + (60 * 60 * schedule.frequency_hours) - ) - end + let!(:recipient_schedule) { schedule.recipient_schedules.for(recipient).first } let!(:not_ready_recipient_schedule) do RecipientSchedule.create!( recipient: recipient, schedule: schedule, - upcoming_question_ids: "#{question.id},3", + upcoming_question_ids: '1,3', attempted_question_ids: '2', last_attempt_at: 1.day.ago, next_attempt_at: 1.day.ago + (60 * 60 * schedule.frequency_hours) @@ -50,7 +47,7 @@ RSpec.describe RecipientSchedule, type: :model do describe 'next_question' do it 'should provide the next question from the upcoming_question_ids list' do - expect(recipient_schedule.next_question).to eq(question) + expect(recipient_schedule.next_question).to eq(questions.first) end end @@ -66,17 +63,17 @@ RSpec.describe RecipientSchedule, type: :model do expect(attempt.recipient).to eq(recipient) expect(attempt.schedule).to eq(schedule) expect(attempt.recipient_schedule).to eq(recipient_schedule) - expect(attempt.question).to eq(question) + expect(attempt.question).to eq(questions.first) expect(attempt.sent_at.to_i).to eq(Time.new.to_i) expect(attempt.answer_index).to be_nil end it 'should update the upcoming_questions_ids' do - expect(recipient_schedule.upcoming_question_ids).to eq('3') + expect(recipient_schedule.upcoming_question_ids).to eq(questions[1..2].map(&:id).join(',')) end it 'should update the attempted_question_ids' do - expect(recipient_schedule.attempted_question_ids).to eq("2,#{question.id}") + expect(recipient_schedule.attempted_question_ids).to eq(questions.first.id.to_s) end it 'should update last_attempt_at' do diff --git a/spec/models/recipient_spec.rb b/spec/models/recipient_spec.rb index 59e894ff..43a9c7d2 100644 --- a/spec/models/recipient_spec.rb +++ b/spec/models/recipient_spec.rb @@ -2,7 +2,6 @@ require 'rails_helper' describe Recipient do describe "Import" do - let(:school) { School.create!(name: 'School') } let(:data) { "name,phone\rJared,111-222-333\rLauren,222-333-4444\rAbby,333-444-5555\r" } let(:file) { instance_double('File', path: 'path') } @@ -15,4 +14,8 @@ describe Recipient do expect(Recipient.all.map(&:school).uniq).to eq([school]) end end + + describe "When Deleted" do + it 'should delete all recipient_schedules' + end end diff --git a/spec/models/schedule_spec.rb b/spec/models/schedule_spec.rb index f5fbc8f9..dc13fe37 100644 --- a/spec/models/schedule_spec.rb +++ b/spec/models/schedule_spec.rb @@ -2,46 +2,61 @@ require 'rails_helper' describe Schedule do - describe "active" do + let!(:school) { School.create!(name: 'School') } - let!(:school) { School.create!(name: 'School') } - let!(:recipient_list) { RecipientList.create!(name: 'Parents', recipient_id_array: [1, 2, 3]) } - let!(:kids_recipient_list) { RecipientList.create!(name: 'Kids', recipient_id_array: [4, 5, 6]) } - let!(:question_list) { QuestionList.create!(name: 'Questions', question_id_array: [1, 2, 3]) } - - let(:default_schedule_params) { - { - school: school, - recipient_list: recipient_list, - question_list: question_list, - name: 'Parents Schedule', - description: 'Schedule for parent questions', - start_date: 1.month.ago, - end_date: 11.months.from_now, - active: true - } - } + let!(:recipients) { create_recipients(school, 3) } + let!(:recipient_list) do + school.recipient_lists.create!(name: 'Parents', recipient_ids: recipients.map(&:id).join(',')) + end - let!(:active_schedule) do - Schedule.create!(default_schedule_params) - end + let!(:kid_recipients) { create_recipients(school, 3) } + let!(:kids_recipient_list) do + school.recipient_lists.create!(name: 'Kids', recipient_ids: kid_recipients.map(&:id).join(',')) + end - let!(:active_schedule_kids) do - Schedule.create!(default_schedule_params.merge!(name: 'Kids Schedule', recipient_list: kids_recipient_list)) - end + let!(:questions) { create_questions(3) } + let!(:question_list) do + QuestionList.create!(name: 'Questions', question_ids: questions.map(&:id).join(',')) + end - let!(:old_schedule) { - Schedule.create!(default_schedule_params.merge!(start_date: 13.month.ago, end_date: 1.months.ago)) + let(:default_schedule_params) { + { + school: school, + recipient_list: recipient_list, + question_list: question_list, + name: 'Parents Schedule', + description: 'Schedule for parent questions', + start_date: 1.month.ago, + end_date: 11.months.from_now, + active: true } + } - let!(:paused_schedule) { - Schedule.create!(default_schedule_params.merge!(active: false)) - } + let!(:active_schedule) do + Schedule.create!(default_schedule_params) + end + + let!(:active_schedule_kids) do + Schedule.create!(default_schedule_params.merge!(name: 'Kids Schedule', recipient_list: kids_recipient_list)) + end + + let!(:old_schedule) { + Schedule.create!(default_schedule_params.merge!(start_date: 13.month.ago, end_date: 1.months.ago)) + } + let!(:paused_schedule) { + Schedule.create!(default_schedule_params.merge!(active: false)) + } + + describe "active" do it 'finds active schedules' do active = Schedule.active expect(active.length).to eq(2) end + end + it 'creates a recipient_schedule for every recipient when created' do + expect(active_schedule.recipient_schedules.length).to eq(3) end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 57dbf744..673e3dd2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -121,3 +121,29 @@ class FakeSMS self.class.messages << Message.new(from, to, body) end end + +def create_recipients(school, count) + recipients = [] + count.times do |i| + recipients << school.recipients.create( + name: "Person#{count}", + phone: "#{count}" * 10, + ) + end + return recipients +end + +def create_questions(count) + questions = [] + count.times do |i| + questions << Question.create( + text: "Question #{i}:#{count}", + option1: "Option #{i}:#{count} A", + option2: "Option #{i}:#{count} B", + option3: "Option #{i}:#{count} C", + option4: "Option #{i}:#{count} D", + option5: "Option #{i}:#{count} E" + ) + end + return questions +end