Skip to content

Commit

Permalink
Merge pull request #57 from nestauk/restructure-database
Browse files Browse the repository at this point in the history
Restructure how content is linked to users
  • Loading branch information
cdccollins authored Dec 9, 2024
2 parents 648dbad + 69fcb4b commit e7c8563
Show file tree
Hide file tree
Showing 28 changed files with 176 additions and 226 deletions.
2 changes: 1 addition & 1 deletion app/controllers/contents_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ def destroy
private

def content_params
params.require(:content).permit(:body, :link, :position, :welcome_message)
params.require(:content).permit(:body, :link, :position, :age_in_months)
end
end
4 changes: 2 additions & 2 deletions app/controllers/groups_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class GroupsController < ApplicationController
def index
@groups = Group.order(:age_in_months)
@groups = Group.all
end

def show
Expand Down Expand Up @@ -44,6 +44,6 @@ def destroy
private

def group_params
params.require(:group).permit(:name, :age_in_months)
params.require(:group).permit(:name)
end
end
6 changes: 2 additions & 4 deletions app/jobs/send_bulk_message_job.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
class SendBulkMessageJob < ApplicationJob
queue_as :default

def perform(users, group)
return unless group.present?

message_jobs = users.map { |user| SendMessageJob.new(user, group) }
def perform(users)
message_jobs = users.map { |user| SendMessageJob.new(user) }

ActiveJob.perform_all_later(message_jobs)
end
Expand Down
17 changes: 14 additions & 3 deletions app/jobs/send_message_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ class SendMessageJob < ApplicationJob

queue_as :default

def perform(user, group)
content = user.next_content(group)
def perform(user)
content = user.next_content

return unless content.present?
# If BulkMessage fails and reruns this job, don't send them the next message
Expand All @@ -18,6 +18,17 @@ def perform(user, group)
m.content = content
end

Twilio::Client.new.send_message(message) if message.save
Twilio::Client.new.send_message(message) if save_user_and_message(user, message, content)
end

private

def save_user_and_message(user, message, content)
ActiveRecord::Base.transaction do
user.update(last_content_id: content.id)
message.save
rescue ActiveRecord::RecordInvalid
false
end
end
end
21 changes: 5 additions & 16 deletions app/jobs/send_welcome_message_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,12 @@ class SendWelcomeMessageJob < ApplicationJob
queue_as :default

def perform(user)
group = Group.find_by(age_in_months: user.child_age_in_months_today)

message = if group.present? && group.welcome_message.present?
Message.create do |m|
m.token = m.send(:generate_token)
m.link = group.welcome_message.link
m.user = user
m.body = group.welcome_message.body.gsub("{{link}}", track_link_url(m.token))
end
else
Message.create do |m|
m.token = m.send(:generate_token)
m.user = user
m.body = "Welcome to Tiny Happy People, a programme of weekly texts with fun activities! You'll receive your first activity soon."
end
message = Message.build do |m|
m.token = m.send(:generate_token)
m.user = user
m.body = Content::WELCOME_MESSAGE
end

Twilio::Client.new.send_message(message)
Twilio::Client.new.send_message(message) if message.save
end
end
4 changes: 3 additions & 1 deletion app/models/content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ class Content < ApplicationRecord

has_many :messages, dependent: :nullify

validates_presence_of :body, :link
validates_presence_of :body, :link, :age_in_months

WELCOME_MESSAGE = "Hi [Parent], welcome to our programme of weekly texts with fun activities for [Child]’s development. Congrats on starting this amazing journey with your little one!"
end
10 changes: 1 addition & 9 deletions app/models/group.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
class Group < ApplicationRecord
has_many :contents, dependent: :destroy

validates :name, :age_in_months, presence: true

def welcome_message
contents.find_by(welcome_message: true)
end

def weekly_content
contents.where(welcome_message: false)
end
validates :name, presence: true
end
33 changes: 29 additions & 4 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,38 @@ def full_name
"#{first_name} #{last_name}"
end

def next_content(group)
return unless group.present?
# find lowest ranked content minus any they have already seen
(group.weekly_content - contents).min_by(&:position)
def next_content
if had_any_content_before?
find_next_unseen_content
else
Content.where(age_in_months: child_age_in_months_today).min_by(&:position)
end
end

def had_content_this_week?
messages.where("created_at > ?", 6.days.ago).where.not(content: nil).exists?
end

private

def had_any_content_before?
last_content_id.present?
end

def not_seen_content?(content)
messages.where(content_id: content.id).none?
end

def find_next_unseen_content
i = Content.find(last_content_id).position + 1

loop do
content = Content.find_by(position: i)
# Last message in series
return nil if content.nil?
# Next message
return content if not_seen_content?(content)
i += 1
end
end
end
4 changes: 2 additions & 2 deletions app/views/contents/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
</div>

<div class="mt-3">
<%= form.input :link %>
<%= form.input :age_in_months, wrapper_html: { class: "w-1/2" } %>
</div>

<div class="mt-3">
<%= form.input :welcome_message, label: "This is the welcome message", input_html: { class: "form-checkbox" } %>
<%= form.input :link %>
</div>

<%= form.submit (action_name == "edit" ? "Update" : "Create"), class: "btn btn-primary mt-5 bg-purple-600 text-white w-full" %>
Expand Down
4 changes: 0 additions & 4 deletions app/views/groups/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@
<%= form.input :name %>
</div>

<div class="mt-3">
<%= form.input :age_in_months %>
</div>

<%= form.submit (action_name == "edit" ? "Update" : "Create"), class: "btn btn-primary mt-5 bg-purple-600 text-white w-full" %>
<% end %>
2 changes: 0 additions & 2 deletions app/views/groups/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<thead>
<tr class="px-4 py-4 bg-purple-300 text-left">
<th class="p-4 w-3/4">Content group name</th>
<th class="p-4 w-3/4">Child age</th>
<th class="p-4 w-1/5">No. of messages</th>
<th>&nbsp</th>
</tr>
Expand All @@ -18,7 +17,6 @@
<% @groups.each do |group| %>
<tr class="border-b-2">
<td class="p-4"><%= link_to group.name, group, class: "underline hover:no-underline" %></td>
<td class="p-4"><%= group.age_in_months %> months</td>
<td class="p-4"><%= group.contents.size %></td>
<td class="p-4">
<%= link_to "Edit", edit_group_path(group), class: "underline hover:no-underline" %>
Expand Down
15 changes: 7 additions & 8 deletions app/views/groups/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,24 @@
<thead>
<tr class="px-4 py-4 bg-purple-300 text-left">
<th class="p-4"></th>
<th class="p-4 w-3/5">Body</th>
<th class="p-4 w-2/5">Link</th>
<th class="p-4 w-6/12">Body</th>
<th class="p-4 w-1/12">Age</th>
<th class="p-4 w-5/12">Link</th>
<th>&nbsp</th>
</tr>
</thead>
<tbody class="bg-white" data-controller="sortable" data-sortable-handle-value=".cursor-grab">
<% @group.contents.order(:position).each do |content| %>
<tr class="border-b-2" data-sortable-update-url="<%= update_position_path(content) %>">
<td class="p-4">
<% unless content.welcome_message? %>
<svg class="cursor-grab" width="20px" height="20px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ccc"><path d="M3 5H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 19H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
<% end %>
<svg class="cursor-grab" width="20px" height="20px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ccc"><path d="M3 5H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 19H21" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</td>
<td class="p-4">
<% if content.welcome_message? %>
<span class="text-sm text-gray-500">Welcome message</span><br>
<% end %>
<%= content.body %>
</td>
<td class="p-4">
<%= content.age_in_months %>
</td>
<td class="p-4"><%= content.link %></td>
<td class="p-4">
<%= link_to "Edit", edit_group_content_path(@group, content), class: "underline hover:no-underline" %>
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20241205095730_add_last_content_to_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddLastContentToUser < ActiveRecord::Migration[7.2]
def change
add_reference :users, :last_content, foreign_key: {to_table: :contents}
end
end
12 changes: 12 additions & 0 deletions db/migrate/20241205100911_remove_age_from_group.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RemoveWelcomeMessageFromContent < ActiveRecord::Migration[8.0]
def change
remove_column :contents, :welcome_message, :boolean
end
end
8 changes: 5 additions & 3 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 8 additions & 24 deletions lib/tasks/scheduler.rake
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,35 @@ 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

desc "Send afternoon text message"
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

desc "Send evening text message"
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

desc "Send no timing preference text message"
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

Expand Down
1 change: 1 addition & 0 deletions test/factories/content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
factory :content do
body { "Sample Body" }
link { "www.example.com" }
age_in_months { 18 }
sequence(:position) { |n| n }

group
Expand Down
3 changes: 1 addition & 2 deletions test/factories/group.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
FactoryBot.define do
factory :group do
name { "Content for 18 month olds" }
age_in_months { 18 }
name { "Group" }
end
end
13 changes: 2 additions & 11 deletions test/jobs/send_bulk_message_job_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit e7c8563

Please sign in to comment.