Add a new join model to link summaries with users

So that changing the set of events does not result in not being able to find a generated highlight summary
This commit is contained in:
Jorge Manrubia
2025-10-06 12:00:49 +02:00
parent 5340f73bbb
commit 3e67da7d0e
8 changed files with 141 additions and 13 deletions
+2
View File
@@ -3,6 +3,8 @@
# so that we reuse the same summary for users with different time zones or different accesses as long as the activity
# is the same. This is important to keep AI costs down.
class PeriodHighlights < ApplicationRecord
has_many :weekly_highlights, class_name: "User::WeeklyHighlights", dependent: :destroy
class << self
def create_or_find_for(collections, starts_at:, duration: 1.week)
self.for(collections, starts_at:, duration:) || create_for(collections, starts_at:, duration:)
+19 -4
View File
@@ -1,6 +1,10 @@
module User::Highlights
extend ActiveSupport::Concern
included do
has_many :weekly_highlights, class_name: "User::WeeklyHighlights", dependent: :destroy
end
class_methods do
def generate_all_weekly_highlights_later
User::Highlights::GenerateAllJob.perform_later
@@ -15,20 +19,31 @@ module User::Highlights
def generate_weekly_highlights(date = Time.current)
in_time_zone do
date = date - 1.day if date.sunday?
PeriodHighlights.create_or_find_for collections, starts_at: highlights_starts_at(date), duration: 1.week
weekly_highlights_for(date) || create_weekly_highlights_for(date)
end
end
def weekly_highlights_for(date)
in_time_zone do
PeriodHighlights.for(collections, starts_at: highlights_starts_at(date), duration: 1.week)
weekly_highlights.find_by(starts_at: highlights_starts_at(date))&.period_highlights
end
end
private
def create_weekly_highlights_for(date)
date = date - 1.day if date.sunday?
# Outside of transaction as generating highlights can be a slow operation
PeriodHighlights.create_or_find_for(collections, starts_at: highlights_starts_at(date), duration: 1.week).tap do |period_highlights|
if period_highlights
puts "Generating highlights for #{date}: #{period_highlights.inspect}"
weekly_highlights.create! period_highlights: period_highlights, starts_at: highlights_starts_at(date)
end
end
end
def highlights_starts_at(date = Time.current)
date = date.in_time_zone(timezone)
date.beginning_of_week(:sunday)
date.beginning_of_week(:sunday).to_date
end
end
+6
View File
@@ -0,0 +1,6 @@
# This acts as a join table between users and period_highlights so that we can reuse the
# same highlights for different users.
class User::WeeklyHighlights < ApplicationRecord
belongs_to :user
belongs_to :period_highlights
end
@@ -1,3 +1,4 @@
<p>REQUESTING WEEKLY HIGHLIGHTS FOR <%= day_timeline.week_starts_at %>: <%= day_timeline.week_starts_at - 1.week%></p>
<% if day_timeline.has_weekly_highlights? %>
<h2 class="events__day-header">
<span class="events__day-time min-width max-width">
@@ -0,0 +1,13 @@
class CreateUserHighlights < ActiveRecord::Migration[8.1]
def change
create_table :user_weekly_highlights do |t|
t.references :user, null: false, foreign_key: true
t.references :period_highlights, null: false, foreign_key: true
t.date :starts_at, null: false
t.timestamps
t.index %i[ user_id starts_at ], unique: true
end
end
end
Generated
+14 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2025_10_03_205559) do
ActiveRecord::Schema[8.1].define(version: 2025_10_06_091442) do
create_table "accesses", force: :cascade do |t|
t.datetime "accessed_at"
t.integer "collection_id", null: false
@@ -447,6 +447,17 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_03_205559) do
t.index ["user_id"], name: "index_user_settings_on_user_id"
end
create_table "user_weekly_highlights", force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "period_highlights_id", null: false
t.date "starts_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", null: false
t.index ["period_highlights_id"], name: "index_user_weekly_highlights_on_period_highlights_id"
t.index ["user_id", "starts_at"], name: "index_user_weekly_highlights_on_user_id_and_starts_at", unique: true
t.index ["user_id"], name: "index_user_weekly_highlights_on_user_id"
end
create_table "users", force: :cascade do |t|
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
@@ -548,6 +559,8 @@ ActiveRecord::Schema[8.1].define(version: 2025_10_03_205559) do
add_foreign_key "taggings", "cards"
add_foreign_key "taggings", "tags"
add_foreign_key "user_settings", "users"
add_foreign_key "user_weekly_highlights", "period_highlights", column: "period_highlights_id"
add_foreign_key "user_weekly_highlights", "users"
add_foreign_key "watches", "cards"
add_foreign_key "watches", "users"
add_foreign_key "webhook_delinquency_trackers", "webhooks"
+84 -7
View File
@@ -464,12 +464,12 @@ columns:
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: due_on
cast_type: !ruby/object:ActiveRecord::Type::Date
cast_type: &37 !ruby/object:ActiveRecord::Type::Date
precision:
scale:
limit:
timezone:
sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
sql_type_metadata: &38 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
sql_type: date
type: :date
limit:
@@ -1268,8 +1268,33 @@ columns:
comment:
- *9
- *18
user_weekly_highlights:
- *5
- *6
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: period_highlights_id
cast_type: *3
sql_type_metadata: *4
'null': false
default:
default_function:
collation:
comment:
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: starts_at
cast_type: *37
sql_type_metadata: *38
'null': false
default:
default_function:
collation:
comment:
- *9
- *18
users:
- &38 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
- &40 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: active
cast_type: *32
@@ -1353,7 +1378,7 @@ columns:
comment:
- *6
- *9
- &37 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
- &39 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
name: webhook_id
cast_type: *3
@@ -1407,9 +1432,9 @@ columns:
collation:
comment:
- *9
- *37
- *39
webhooks:
- *38
- *40
- *24
- *5
- *6
@@ -1533,6 +1558,7 @@ primary_keys:
taggings: id
tags: id
user_settings: id
user_weekly_highlights: id
users: id
watches: id
webhook_delinquency_trackers: id
@@ -1589,6 +1615,7 @@ data_sources:
taggings: true
tags: true
user_settings: true
user_weekly_highlights: true
users: true
watches: true
webhook_delinquency_trackers: true
@@ -3071,6 +3098,56 @@ indexes:
nulls_not_distinct:
comment:
valid: true
user_weekly_highlights:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: user_weekly_highlights
name: index_user_weekly_highlights_on_period_highlights_id
unique: false
columns:
- period_highlights_id
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: user_weekly_highlights
name: index_user_weekly_highlights_on_user_id
unique: false
columns:
- user_id
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: user_weekly_highlights
name: index_user_weekly_highlights_on_user_id_and_starts_at
unique: true
columns:
- user_id
- starts_at
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
users:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: users
@@ -3222,4 +3299,4 @@ indexes:
comment:
valid: true
workflows: []
version: 20251003205559
version: 20251006091442
@@ -2,11 +2,12 @@
require_relative "../config/environment"
WEEKS_TO_BACKFILL = 10
WEEKS_TO_BACKFILL = 3
ActiveRecord::Base.logger = Logger.new(File::NULL)
ApplicationRecord.with_each_tenant do |tenant|
PeriodHighlights.destroy_all
WEEKS_TO_BACKFILL.times do |index|
User.active.find_each do |user|
user.generate_weekly_highlights(Time.current - index.weeks)