- <% unless content.welcome_message? %>
-
- <% end %>
+
|
- <% if content.welcome_message? %>
- Welcome message
- <% end %>
<%= content.body %>
|
+
+ <%= content.age_in_months %>
+ |
<%= content.link %> |
<%= link_to "Edit", edit_group_content_path(@group, content), class: "underline hover:no-underline" %>
diff --git a/db/migrate/20241205095730_add_last_content_to_user.rb b/db/migrate/20241205095730_add_last_content_to_user.rb
new file mode 100644
index 0000000..22a89dc
--- /dev/null
+++ b/db/migrate/20241205095730_add_last_content_to_user.rb
@@ -0,0 +1,5 @@
+class AddLastContentToUser < ActiveRecord::Migration[7.2]
+ def change
+ add_reference :users, :last_content, foreign_key: {to_table: :contents}
+ end
+end
diff --git a/db/migrate/20241205100911_remove_age_from_group.rb b/db/migrate/20241205100911_remove_age_from_group.rb
new file mode 100644
index 0000000..b7c9c92
--- /dev/null
+++ b/db/migrate/20241205100911_remove_age_from_group.rb
@@ -0,0 +1,12 @@
+class RemoveAgeFromGroup < ActiveRecord::Migration[7.2]
+ def change
+ add_column :contents, :age_in_months, :integer
+
+ Content.find_each do |content|
+ content.update(age_in_months: content.group.age_in_months)
+ end
+
+ remove_column :groups, :age_in_months, :integer
+ change_column_null :contents, :age_in_months, false
+ end
+end
diff --git a/db/migrate/20241206110738_remove_welcome_message_from_content.rb b/db/migrate/20241206110738_remove_welcome_message_from_content.rb
new file mode 100644
index 0000000..c36615b
--- /dev/null
+++ b/db/migrate/20241206110738_remove_welcome_message_from_content.rb
@@ -0,0 +1,5 @@
+class RemoveWelcomeMessageFromContent < ActiveRecord::Migration[8.0]
+ def change
+ remove_column :contents, :welcome_message, :boolean
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5d4ff99..1575ca1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2024_12_05_140053) do
+ActiveRecord::Schema[8.0].define(version: 2024_12_06_110738) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@@ -96,7 +96,7 @@
t.string "link"
t.bigint "group_id"
t.integer "position", null: false
- t.boolean "welcome_message", default: false
+ t.integer "age_in_months", null: false
t.index ["group_id", "position"], name: "index_contents_on_group_id_and_position", unique: true
t.index ["group_id"], name: "index_contents_on_group_id"
end
@@ -118,7 +118,6 @@
create_table "groups", force: :cascade do |t|
t.string "name", null: false
- t.integer "age_in_months", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@@ -169,9 +168,12 @@
t.datetime "restart_at"
t.integer "adjust_amount", default: 0
t.datetime "nudged_at"
+ t.bigint "last_content_id"
+ t.index ["last_content_id"], name: "index_users_on_last_content_id"
end
add_foreign_key "clicks", "pages"
add_foreign_key "interests", "users"
add_foreign_key "messages", "contents"
+ add_foreign_key "users", "contents", column: "last_content_id"
end
diff --git a/lib/tasks/scheduler.rake b/lib/tasks/scheduler.rake
index d5914bd..937a9d2 100644
--- a/lib/tasks/scheduler.rake
+++ b/lib/tasks/scheduler.rake
@@ -3,12 +3,8 @@ namespace :scheduler do
task send_morning_message: :environment do
(next unless Date.today.wday == ENV.fetch("WEEKLY_MESSAGE_DAY").to_i) if ENV.fetch("SET_WEEKLY") == "true"
- User.contactable.wants_morning_message.group_by(&:adjusted_child_age_in_months_today).each do |age, users|
- group = Group.find_by(age_in_months: age)
-
- next unless group
-
- SendBulkMessageJob.perform_later(users, group)
+ User.contactable.wants_morning_message.find_in_batches do |users|
+ SendBulkMessageJob.perform_later(users)
end
end
@@ -16,12 +12,8 @@ namespace :scheduler do
task send_afternoon_message: :environment do
(next unless Date.today.wday == ENV.fetch("WEEKLY_MESSAGE_DAY").to_i) if ENV.fetch("SET_WEEKLY") == "true"
- User.contactable.wants_afternoon_message.group_by(&:adjusted_child_age_in_months_today).each do |age, users|
- group = Group.find_by(age_in_months: age)
-
- next unless group
-
- SendBulkMessageJob.perform_later(users, group)
+ User.contactable.wants_afternoon_message.find_in_batches do |users|
+ SendBulkMessageJob.perform_later(users)
end
end
@@ -29,12 +21,8 @@ namespace :scheduler do
task send_evening_message: :environment do
(next unless Date.today.wday == ENV.fetch("WEEKLY_MESSAGE_DAY").to_i) if ENV.fetch("SET_WEEKLY") == "true"
- User.contactable.wants_evening_message.group_by(&:adjusted_child_age_in_months_today).each do |age, users|
- group = Group.find_by(age_in_months: age)
-
- next unless group
-
- SendBulkMessageJob.perform_later(users, group)
+ User.contactable.wants_evening_message.find_in_batches do |users|
+ SendBulkMessageJob.perform_later(users)
end
end
@@ -42,12 +30,8 @@ namespace :scheduler do
task send_no_timing_preference_message: :environment do
(next unless Date.today.wday == ENV.fetch("WEEKLY_MESSAGE_DAY").to_i) if ENV.fetch("SET_WEEKLY") == "true"
- User.contactable.no_preference_message.group_by(&:adjusted_child_age_in_months_today).each do |age, users|
- group = Group.find_by(age_in_months: age)
-
- next unless group
-
- SendBulkMessageJob.perform_later(users, group)
+ User.contactable.no_preference_message.find_in_batches do |users|
+ SendBulkMessageJob.perform_later(users)
end
end
diff --git a/test/factories/content.rb b/test/factories/content.rb
index 0e0c357..fb94eab 100644
--- a/test/factories/content.rb
+++ b/test/factories/content.rb
@@ -2,6 +2,7 @@
factory :content do
body { "Sample Body" }
link { "www.example.com" }
+ age_in_months { 18 }
sequence(:position) { |n| n }
group
diff --git a/test/factories/group.rb b/test/factories/group.rb
index 8090ff9..bb5dfc2 100644
--- a/test/factories/group.rb
+++ b/test/factories/group.rb
@@ -1,6 +1,5 @@
FactoryBot.define do
factory :group do
- name { "Content for 18 month olds" }
- age_in_months { 18 }
+ name { "Group" }
end
end
diff --git a/test/jobs/send_bulk_message_job_test.rb b/test/jobs/send_bulk_message_job_test.rb
index 42a0534..3ac8ddc 100644
--- a/test/jobs/send_bulk_message_job_test.rb
+++ b/test/jobs/send_bulk_message_job_test.rb
@@ -5,20 +5,11 @@ class SendBulkMessageJobTest < ActiveSupport::TestCase
test "#perform creates jobs to send messages to users" do
users = create_list(:user, 3, child_birthday: 3.months.ago)
- group = create(:group, age_in_months: 3)
+ group = create(:group)
create(:content, group:)
assert_enqueued_jobs 3 do
- SendBulkMessageJob.perform_now(users, group)
- end
- end
-
- test "#perform does not create jobs if group is not present" do
- users = create_list(:user, 3, child_birthday: 3.months.ago)
- group = nil
-
- assert_no_enqueued_jobs do
- SendBulkMessageJob.perform_now(users, group)
+ SendBulkMessageJob.perform_now(users)
end
end
end
diff --git a/test/jobs/send_message_job_test.rb b/test/jobs/send_message_job_test.rb
index c618720..637a57e 100644
--- a/test/jobs/send_message_job_test.rb
+++ b/test/jobs/send_message_job_test.rb
@@ -5,48 +5,64 @@ class SendMessageJobTest < ActiveSupport::TestCase
include Rails.application.routes.url_helpers
test "#perform sends messages with default content" do
- user = create(:user)
- group = create(:group, age_in_months: user.child_age_in_months_today)
- content = create(:content, group:, body: "here is a link: {{link}}")
+ content = create(:content, body: "here is a link: {{link}}")
+ content2 = create(:content, group: content.group, body: "here is a link: {{link}}")
+ user = create(:user, last_content_id: content.id)
Message.any_instance.stubs(:generate_token).returns("123")
- stub_successful_twilio_call(content.body.gsub("{{link}}", track_link_url("123")), user)
+ stub_successful_twilio_call(content2.body.gsub("{{link}}", track_link_url("123")), user)
- SendMessageJob.new.perform(user, group)
+ SendMessageJob.new.perform(user)
assert_equal 1, Message.count
- assert_equal content, Message.last.content
+ assert_equal content2, Message.last.content
assert_match(/m\/123/, Message.last.body)
+ assert_equal content2.id, user.reload.last_content_id
end
test "#perform does not send message if no appropriate content available" do
assert_no_changes -> { Message.count } do
- SendMessageJob.new.perform(create(:user), create(:group))
+ SendMessageJob.new.perform(create(:user))
end
end
test "#perform does not send message if user already has the same message" do
user = create(:user)
- group = create(:group, age_in_months: user.child_age_in_months_today)
- content = create(:content, group: group)
+ group = create(:group)
+ content = create(:content, group:)
- create(:message, user: user, content: content)
+ create(:message, user:, content:)
assert_no_changes -> { Message.count } do
- SendMessageJob.new.perform(user, group)
+ SendMessageJob.new.perform(user)
end
end
test "#perform does not send message if user has had a message this week" do
user = create(:user)
- group = create(:group, age_in_months: user.child_age_in_months_today)
- content = create(:content, group: group)
+ group = create(:group)
+ content = create(:content, group:)
create(:message, user: user, content: content, created_at: 1.day.ago)
assert_no_changes -> { Message.count } do
- SendMessageJob.new.perform(user, group)
+ SendMessageJob.new.perform(user)
end
end
+
+ test "#perform does not send message if user fails to update" do
+ content = create(:content, body: "here is a link: {{link}}")
+ create(:content, group: content.group, body: "here is a link: {{link}}")
+ user = create(:user, last_content_id: content.id)
+
+ Message.any_instance.stubs(:generate_token).returns("123")
+ User.any_instance.stubs(:update).raises(ActiveRecord::RecordInvalid)
+
+ assert_no_changes -> { Message.count } do
+ SendMessageJob.new.perform(user)
+ end
+
+ assert_equal content.id, user.reload.last_content_id
+ end
end
diff --git a/test/jobs/send_welcome_message_job_test.rb b/test/jobs/send_welcome_message_job_test.rb
index 7fe654b..1e21e2e 100644
--- a/test/jobs/send_welcome_message_job_test.rb
+++ b/test/jobs/send_welcome_message_job_test.rb
@@ -6,50 +6,12 @@ class SendWelcomeMessageJobTest < ActiveSupport::TestCase
test "#perform sends message with default content" do
user = create(:user, child_birthday: 18.months.ago)
- group = create(:group, age_in_months: 18)
- content = create(:content, group:, welcome_message: true, link: "https://example.com", body: "Hi, {{link}}")
- create(:content, group:, welcome_message: false, link: "https://example.com")
- Message.any_instance.stubs(:generate_token).returns("123")
-
- stub_successful_twilio_call(content.body.gsub("{{link}}", track_link_url("123")), user)
-
- SendWelcomeMessageJob.new.perform(user)
-
- assert_equal 1, Message.count
- assert_match(/m\/123/, Message.last.body)
- assert_equal "https://example.com", Message.last.link
- end
-
- test "#perform sends message with no link if there isn't appropriate content" do
- user = create(:user, child_birthday: 25.months.ago)
-
- Message.any_instance.stubs(:generate_token).returns("123")
-
- message = "Welcome to Tiny Happy People, a programme of weekly texts with fun activities! You'll receive your first activity soon."
-
- stub_successful_twilio_call(message, user)
-
- SendWelcomeMessageJob.new.perform(user)
-
- assert_equal 1, Message.count
- assert_nil Message.last.link
- end
-
- test "#perform sends message with no link if there isn't a welcome message" do
- user = create(:user, child_birthday: 18.months.ago)
- group = create(:group, age_in_months: 18)
- create(:content, group:, welcome_message: false, link: "https://example.com")
-
- Message.any_instance.stubs(:generate_token).returns("123")
-
- message = "Welcome to Tiny Happy People, a programme of weekly texts with fun activities! You'll receive your first activity soon."
-
- stub_successful_twilio_call(message, user)
+ stub_successful_twilio_call(Content::WELCOME_MESSAGE, user)
SendWelcomeMessageJob.new.perform(user)
assert_equal 1, Message.count
- assert_nil Message.last.link
+ assert_match(Content::WELCOME_MESSAGE, Message.last.body)
end
end
diff --git a/test/lib/scheduler_test.rb b/test/lib/scheduler_test.rb
index 3272d6b..f547917 100644
--- a/test/lib/scheduler_test.rb
+++ b/test/lib/scheduler_test.rb
@@ -14,8 +14,7 @@ class SchedulerTest < ActiveSupport::TestCase
end
test "send_morning_message" do
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "morning")
assert_enqueued_with(job: SendBulkMessageJob) do
@@ -24,8 +23,7 @@ class SchedulerTest < ActiveSupport::TestCase
end
test "send_afternoon_message" do
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "afternoon")
assert_enqueued_with(job: SendBulkMessageJob) do
@@ -34,8 +32,7 @@ class SchedulerTest < ActiveSupport::TestCase
end
test "send_evening_message" do
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "evening")
assert_enqueued_with(job: SendBulkMessageJob) do
@@ -44,8 +41,7 @@ class SchedulerTest < ActiveSupport::TestCase
end
test "send_no_timing_preference_message" do
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "no_preference")
assert_enqueued_with(job: SendBulkMessageJob) do
@@ -54,8 +50,7 @@ class SchedulerTest < ActiveSupport::TestCase
end
test "send_no_timing_preference_message for users with no timing set" do
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: nil)
assert_enqueued_with(job: SendBulkMessageJob) do
@@ -71,21 +66,6 @@ class SchedulerTest < ActiveSupport::TestCase
end
end
- test "no job enqueued if there is no appropriate group" do
- create(:user, timing: "morning")
- create(:user, timing: "afternoon")
- create(:user, timing: "evening")
- create(:user, timing: "no_preference")
- create(:user, timing: nil)
-
- assert_no_enqueued_jobs do
- Rake::Task["scheduler:send_morning_message"].execute
- Rake::Task["scheduler:send_afternoon_message"].execute
- Rake::Task["scheduler:send_evening_message"].execute
- Rake::Task["scheduler:send_no_timing_preference_message"].execute
- end
- end
-
test "restart_users" do
user = create(:user, contactable: false, restart_at: Time.now - 1.day)
user2 = create(:user, contactable: false, restart_at: Time.now + 1.day)
@@ -132,8 +112,7 @@ class SchedulerTest < ActiveSupport::TestCase
test "doesn't run unless it's the right day" do
travel_to_tuesday
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "morning")
assert_no_enqueued_jobs do
@@ -145,8 +124,7 @@ class SchedulerTest < ActiveSupport::TestCase
ENV["SET_WEEKLY"] = "false"
travel_to_tuesday
- group = create(:group, age_in_months: 18)
- create(:content, group:)
+ create(:content)
create(:user, timing: "morning")
assert_enqueued_with(job: SendBulkMessageJob) do
diff --git a/test/models/group_test.rb b/test/models/group_test.rb
index 6a9710a..00c2f25 100644
--- a/test/models/group_test.rb
+++ b/test/models/group_test.rb
@@ -10,19 +10,4 @@ def setup
end
test("name required") { assert_present(:name) }
- test("age_in_months required") { assert_present(:age_in_months) }
-
- test "#weekly_content" do
- weekly_content = create(:content, group: @subject)
- create(:content, group: @subject, welcome_message: true)
-
- assert_equal [weekly_content], @subject.weekly_content
- end
-
- test "#welcome_message" do
- welcome_message = create(:content, group: @subject, welcome_message: true)
- create(:content, group: @subject, welcome_message: false)
-
- assert_equal welcome_message, @subject.welcome_message
- end
end
diff --git a/test/models/user_test.rb b/test/models/user_test.rb
index 6f6a802..45ac687 100644
--- a/test/models/user_test.rb
+++ b/test/models/user_test.rb
@@ -126,24 +126,43 @@ class UserTest < ActiveSupport::TestCase
end
test "#next_content method returns next ranked content for age group" do
- group = create(:group, age_in_months: @subject.child_age_in_months_today)
- create(:content, group:, welcome_message: true)
+ group = create(:group)
content1 = create(:content, group:, position: 1)
content2 = create(:content, group:, position: 2)
- create(:message, content: content1, user: @subject)
+ create(:content, group:, position: 3)
+ @subject.update(last_content_id: content1.id)
- assert_equal @subject.next_content(group), content2
+ assert_equal @subject.next_content, content2
end
test "#next_content method returns nothing if no appropriate content" do
- group = create(:group, age_in_months: @subject.child_age_in_months_today)
- create(:content, group:, welcome_message: true)
+ group = create(:group)
+ create(:content, group:, position: 1)
+ content2 = create(:content, group:, position: 2)
+ @subject.update(last_content_id: content2.id)
+
+ assert_nil @subject.next_content
+ end
+
+ test "#next_content finds appropriate content if user has not had content before" do
+ group = create(:group)
+ content = create(:content, group:, position: 1, age_in_months: 18)
+ create(:content, group:, position: 2, age_in_months: 18)
+ create(:content, group:, position: 3, age_in_months: 19)
+
+ assert_equal @subject.next_content, content
+ end
+
+ test "#next_content does not return content that the user has already seen" do
+ # This is in case the content order has been switched around by the admins
+ group = create(:group)
content1 = create(:content, group:, position: 1)
content2 = create(:content, group:, position: 2)
- create(:message, content: content1, user: @subject)
- create(:message, content: content2, user: @subject)
+ content3 = create(:content, group:, position: 3)
+ create(:message, user: @subject, content: content2)
+ @subject.update(last_content_id: content1.id)
- assert_nil @subject.next_content(group)
+ assert_equal @subject.next_content, content3
end
test "#had_content_this_week? method returns true if user has had content" do
diff --git a/test/system/contents_test.rb b/test/system/contents_test.rb
index 08f7b5e..e07e751 100644
--- a/test/system/contents_test.rb
+++ b/test/system/contents_test.rb
@@ -16,30 +16,13 @@ class ContentsTest < ApplicationSystemTestCase
fill_in "Body", with: "New content"
fill_in "Link", with: "www.example.com"
+ fill_in "Age in months", with: "18"
click_on "Create"
assert_text "Content for message was successfully created"
assert_text "New content"
end
- test "creating new content with welcome message" do
- sign_in
- visit group_path(@group)
-
- assert_text @group.name
-
- click_on "Add new message"
-
- fill_in "Body", with: "New content"
- fill_in "Link", with: "www.example.com"
- check "This is the welcome message"
- click_on "Create"
-
- within("tr", text: "New content") do
- assert_text "Welcome message"
- end
- end
-
test "shows errors" do
create(:content, body: "Old Content", group: @group)
@@ -52,6 +35,7 @@ class ContentsTest < ApplicationSystemTestCase
assert_field_has_errors("Body")
assert_field_has_errors("Link")
+ assert_field_has_errors("Age in months")
end
test "updating content" do
diff --git a/test/system/groups_test.rb b/test/system/groups_test.rb
index bf2c658..078645b 100644
--- a/test/system/groups_test.rb
+++ b/test/system/groups_test.rb
@@ -12,7 +12,6 @@ class GroupsTest < ApplicationSystemTestCase
click_on "Create content group"
fill_in "Name", with: "Default content"
- fill_in "Age in months", with: "17"
click_on "Create"
assert_text "Content group successfully created"
@@ -28,7 +27,6 @@ class GroupsTest < ApplicationSystemTestCase
click_on "Create"
assert_field_has_errors("Name")
- assert_field_has_errors("Age in months")
end
test "updating a group" do
diff --git a/test/system/users_test.rb b/test/system/users_test.rb
index 29e06b4..85cbce0 100644
--- a/test/system/users_test.rb
+++ b/test/system/users_test.rb
@@ -15,8 +15,7 @@ class UsersTest < ApplicationSystemTestCase
check "Would you like to be added to a Slack channel with other parents to discuss the programme?"
check "I accept the terms of service and privacy policy"
- message = "Welcome to Tiny Happy People, a programme of weekly texts with fun activities! You'll receive your first activity soon."
- stub_successful_twilio_call(message, User.new(phone_number: "+447444930200"))
+ stub_successful_twilio_call(Content::WELCOME_MESSAGE, User.new(phone_number: "+447444930200"))
within("#sign-up-form") do
click_on "Sign up"
@@ -35,7 +34,7 @@ class UsersTest < ApplicationSystemTestCase
assert_text "ABC123"
assert_text "Does have family support"
assert_text "On Slack"
- assert_text "Welcome to Tiny Happy People, a programme of weekly texts with fun activities!"
+ assert_text Content::WELCOME_MESSAGE
end
test "form shows errors" do
|