record user fav scans
This commit is contained in:
@@ -86,38 +86,16 @@ class Domain::Fa::Job::FavsJob < Domain::Fa::Job::Base
|
||||
end
|
||||
|
||||
faved_post_ids_to_add = faved_post_ids - existing_faved_post_ids
|
||||
upsert_faved_post_ids(user:, post_ids: faved_post_ids_to_add)
|
||||
user.upsert_new_favs(
|
||||
faved_post_ids_to_add.to_a,
|
||||
log_entry: causing_log_entry,
|
||||
)
|
||||
ensure
|
||||
user.save! if user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(user: Domain::User::FaUser, post_ids: T::Set[Integer]).void }
|
||||
def upsert_faved_post_ids(user:, post_ids:)
|
||||
ReduxApplicationRecord.transaction do
|
||||
if post_ids.any?
|
||||
post_ids.each_slice(1000) do |slice|
|
||||
Domain::UserPostFav.upsert_all(
|
||||
slice.map { |id| { user_id: user.id, post_id: id } },
|
||||
unique_by: %i[user_id post_id],
|
||||
)
|
||||
end
|
||||
|
||||
# Use reset_counters to update the counter cache after using upsert_all
|
||||
Domain::User.reset_counters(user.id, :user_post_favs)
|
||||
logger.info(
|
||||
format_tags(
|
||||
make_tag("reset user_post_favs counter cache for user", user.id),
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
user.scanned_favs_at = Time.zone.now
|
||||
end
|
||||
logger.info(format_tags(make_tag("total new favs", post_ids.size)))
|
||||
end
|
||||
|
||||
module ScanPageResult
|
||||
extend T::Sig
|
||||
|
||||
|
||||
@@ -150,21 +150,17 @@ class Domain::Fa::Job::UserPageJob < Domain::Fa::Job::Base
|
||||
|
||||
if recent_faved_fa_ids.empty?
|
||||
logger.info(format_tags("skipping favs scan, 0 favorites"))
|
||||
user.scanned_favs_at = Time.current
|
||||
user.upsert_new_favs([], log_entry: causing_log_entry)
|
||||
elsif recent_faved_fa_ids.count < 8
|
||||
logger.info(
|
||||
format_tags(
|
||||
"skipping favs scan, #{recent_faved_fa_ids.count} favorites < threshold",
|
||||
),
|
||||
)
|
||||
user.user_post_favs.upsert_all(
|
||||
recent_faved_posts.map(&:id).compact.map { |post_id| { post_id: } },
|
||||
unique_by: %i[user_id post_id],
|
||||
user.upsert_new_favs(
|
||||
recent_faved_posts.map(&:id).compact,
|
||||
log_entry: causing_log_entry,
|
||||
)
|
||||
|
||||
# Use reset_counters to update the counter cache after upserting favs
|
||||
Domain::User.reset_counters(user.id, :user_post_favs)
|
||||
user.scanned_favs_at = Time.current
|
||||
elsif user.scanned_favs_at.present?
|
||||
known_faved_post_fa_ids =
|
||||
user
|
||||
@@ -203,22 +199,16 @@ class Domain::Fa::Job::UserPageJob < Domain::Fa::Job::Base
|
||||
"unknown favs are all at beginning, adding fa_ids to favs: #{unknown_fav_fa_ids.join(", ")}",
|
||||
)
|
||||
|
||||
if unknown_fav_fa_ids.any?
|
||||
unknown_faved_posts =
|
||||
unknown_fav_fa_ids
|
||||
.map do |fa_id|
|
||||
recent_faved_posts.find { |post| post.fa_id == fa_id }
|
||||
end
|
||||
.compact
|
||||
|
||||
user.user_post_favs.upsert_all(
|
||||
unknown_faved_posts.map(&:id).compact.map { |post_id| { post_id: } },
|
||||
unique_by: %i[user_id post_id],
|
||||
)
|
||||
Domain::User.reset_counters(user.id, :user_post_favs)
|
||||
end
|
||||
|
||||
user.scanned_favs_at = Time.current
|
||||
user.upsert_new_favs(
|
||||
unknown_fav_fa_ids
|
||||
.map do |fa_id|
|
||||
recent_faved_posts.find { |post| post.fa_id == fa_id }
|
||||
end
|
||||
.compact
|
||||
.map(&:id)
|
||||
.compact,
|
||||
log_entry: causing_log_entry,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ class Domain::User < ReduxApplicationRecord
|
||||
end
|
||||
|
||||
attr_json :migrated_user_favs_at, :datetime
|
||||
attr_json_due_timestamp :scanned_favs_at, 3.months
|
||||
|
||||
has_many :user_search_names,
|
||||
class_name: "::Domain::UserSearchName",
|
||||
@@ -136,6 +137,31 @@ class Domain::User < ReduxApplicationRecord
|
||||
class_name: klass.name
|
||||
end
|
||||
|
||||
has_many :favs_scans,
|
||||
-> { order(created_at: :desc) },
|
||||
inverse_of: :user,
|
||||
class_name: Domain::UserJobEvent::FavsScan.name
|
||||
|
||||
sig do
|
||||
params(post_ids: T::Array[Integer], log_entry: T.nilable(HttpLogEntry)).void
|
||||
end
|
||||
def upsert_new_favs(post_ids, log_entry: nil)
|
||||
self.class.transaction do
|
||||
if post_ids.any?
|
||||
post_ids.each_slice(1000) do |slice|
|
||||
self.user_post_favs.upsert_all(
|
||||
slice.map { |post_id| { post_id: } },
|
||||
unique_by: %i[user_id post_id],
|
||||
)
|
||||
end
|
||||
end
|
||||
self.favs_scans.create!(num_favs_added: post_ids.count, log_entry:)
|
||||
Domain::User.reset_counters(id, :user_post_favs)
|
||||
self.scanned_favs_at = Time.current
|
||||
logger.info(format_tags(make_tag("upsert new favs", post_ids.size)))
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(klass: T.class_of(Domain::User)).void }
|
||||
def self.has_followed_users!(klass)
|
||||
self.class_has_followed_users = klass
|
||||
|
||||
@@ -5,7 +5,6 @@ class Domain::User::E621User < Domain::User
|
||||
attr_json :favs_are_hidden, :boolean
|
||||
attr_json :num_other_favs_cached, :integer
|
||||
attr_json :scanned_favs_status, :string
|
||||
attr_json_due_timestamp :scanned_favs_at, 1.month
|
||||
attr_json :registered_at, :datetime
|
||||
|
||||
enum :scanned_favs_status, { ok: "ok", error: "error" }, prefix: :scanned_favs
|
||||
|
||||
@@ -21,7 +21,6 @@ class Domain::User::FaUser < Domain::User
|
||||
attr_json_due_timestamp :scanned_page_at, 3.months
|
||||
attr_json_due_timestamp :scanned_follows_at, 3.months
|
||||
attr_json_due_timestamp :scanned_followed_by_at, 3.months
|
||||
attr_json_due_timestamp :scanned_favs_at, 3.months
|
||||
attr_json_due_timestamp :scanned_incremental_at, 1.month
|
||||
attr_json :registered_at, :datetime
|
||||
attr_json :migrated_followed_users_at, :datetime
|
||||
|
||||
10
app/models/domain/user_job_event.rb
Normal file
10
app/models/domain/user_job_event.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# typed: strict
|
||||
class Domain::UserJobEvent < ReduxApplicationRecord
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
include AttrJsonRecordAliases
|
||||
abstract!
|
||||
self.abstract_class = true
|
||||
|
||||
belongs_to :user, class_name: Domain::User.name
|
||||
end
|
||||
6
app/models/domain/user_job_event/favs_scan.rb
Normal file
6
app/models/domain/user_job_event/favs_scan.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# typed: strict
|
||||
class Domain::UserJobEvent::FavsScan < Domain::UserJobEvent
|
||||
self.table_name = "domain_user_job_event_fav_scans"
|
||||
attr_json :num_favs_added, :integer
|
||||
belongs_to :log_entry, class_name: "HttpLogEntry"
|
||||
end
|
||||
@@ -17,4 +17,15 @@ class ActiveRecord::Migration
|
||||
**options,
|
||||
)
|
||||
end
|
||||
|
||||
sig { params(name: String, values: T::Array[String]).void }
|
||||
def create_enum(name, values)
|
||||
reversible do |dir|
|
||||
dir.up do
|
||||
execute "CREATE TYPE #{name} AS ENUM (#{values.map { |t| "'#{t}'" }.join(",")})"
|
||||
end
|
||||
|
||||
dir.down { execute "DROP TYPE #{name}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# typed: strict
|
||||
#
|
||||
class CreateUnifiedDomainTables < ActiveRecord::Migration[7.2]
|
||||
extend T::Sig
|
||||
|
||||
@@ -28,17 +27,6 @@ class CreateUnifiedDomainTables < ActiveRecord::Migration[7.2]
|
||||
|
||||
GROUP_TYPES = %w[Domain::PostGroup::InkbunnyPool Domain::PostGroup::E621Pool]
|
||||
|
||||
sig { params(name: String, values: T::Array[String]).void }
|
||||
def create_enum(name, values)
|
||||
reversible do |dir|
|
||||
dir.up do
|
||||
execute "CREATE TYPE #{name} AS ENUM (#{values.map { |t| "'#{t}'" }.join(",")})"
|
||||
end
|
||||
|
||||
dir.down { execute "DROP TYPE #{name}" }
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def change
|
||||
up_only { execute "SET DEFAULT_TABLESPACE = mirai" }
|
||||
|
||||
12
db/migrate/20250626191434_create_job_events_table.rb
Normal file
12
db/migrate/20250626191434_create_job_events_table.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class CreateJobEventsTable < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
up_only { execute "SET DEFAULT_TABLESPACE = mirai" }
|
||||
|
||||
create_table :domain_user_job_event_fav_scans do |t|
|
||||
t.references :user, null: false, foreign_key: { to_table: :domain_users }
|
||||
t.references :log_entry, foreign_key: { to_table: :http_log_entries }
|
||||
t.jsonb :json_attributes, default: {}
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3040,6 +3040,39 @@ CREATE SEQUENCE public.domain_user_avatars_id_seq
|
||||
ALTER SEQUENCE public.domain_user_avatars_id_seq OWNED BY public.domain_user_avatars.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans; Type: TABLE; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE TABLE public.domain_user_job_event_fav_scans (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
log_entry_id bigint,
|
||||
json_attributes jsonb DEFAULT '{}'::jsonb,
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.domain_user_job_event_fav_scans_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.domain_user_job_event_fav_scans_id_seq OWNED BY public.domain_user_job_event_fav_scans.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_post_creations; Type: TABLE; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
@@ -4722,6 +4755,13 @@ ALTER TABLE ONLY public.domain_twitter_users ALTER COLUMN id SET DEFAULT nextval
|
||||
ALTER TABLE ONLY public.domain_user_avatars ALTER COLUMN id SET DEFAULT nextval('public.domain_user_avatars_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.domain_user_job_event_fav_scans ALTER COLUMN id SET DEFAULT nextval('public.domain_user_job_event_fav_scans_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_search_names id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -5529,6 +5569,14 @@ ALTER TABLE ONLY public.domain_user_avatars
|
||||
ADD CONSTRAINT domain_user_avatars_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans domain_user_job_event_fav_scans_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.domain_user_job_event_fav_scans
|
||||
ADD CONSTRAINT domain_user_job_event_fav_scans_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_search_names domain_user_search_names_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
@@ -7228,6 +7276,20 @@ CREATE UNIQUE INDEX index_domain_twitter_users_on_tw_id ON public.domain_twitter
|
||||
CREATE INDEX index_domain_user_avatars_on_user_id ON public.domain_user_avatars USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_domain_user_job_event_fav_scans_on_log_entry_id; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX index_domain_user_job_event_fav_scans_on_log_entry_id ON public.domain_user_job_event_fav_scans USING btree (log_entry_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_domain_user_job_event_fav_scans_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
|
||||
CREATE INDEX index_domain_user_job_event_fav_scans_on_user_id ON public.domain_user_job_event_fav_scans USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_domain_user_post_creations_on_post_id_and_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
|
||||
--
|
||||
@@ -8532,6 +8594,14 @@ ALTER INDEX public.index_blob_files_on_sha256 ATTACH PARTITION public.index_blob
|
||||
ALTER INDEX public.index_blob_files_on_sha256 ATTACH PARTITION public.index_blob_files_63_on_sha256;
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans fk_rails_0041c99646; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.domain_user_job_event_fav_scans
|
||||
ADD CONSTRAINT fk_rails_0041c99646 FOREIGN KEY (log_entry_id) REFERENCES public.http_log_entries(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_e621_favs fk_rails_0b7ec98aa2; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -8740,6 +8810,14 @@ ALTER TABLE ONLY public.domain_fa_follows
|
||||
ADD CONSTRAINT fk_rails_87bfff6dba FOREIGN KEY (follower_id) REFERENCES public.domain_fa_users(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_user_job_event_fav_scans fk_rails_8d6cca4835; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.domain_user_job_event_fav_scans
|
||||
ADD CONSTRAINT fk_rails_8d6cca4835 FOREIGN KEY (user_id) REFERENCES public.domain_users(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: domain_inkbunny_posts fk_rails_91015c8e4f; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -8923,6 +9001,7 @@ ALTER TABLE ONLY public.domain_twitter_tweets
|
||||
SET search_path TO "$user", public;
|
||||
|
||||
INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20250626191434'),
|
||||
('20250619233027'),
|
||||
('20250321050628'),
|
||||
('20250310001341'),
|
||||
|
||||
16
sorbet/rbi/dsl/domain/fa/enqueue_unscanned_ok_posts.rbi
generated
Normal file
16
sorbet/rbi/dsl/domain/fa/enqueue_unscanned_ok_posts.rbi
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for dynamic methods in `Domain::Fa::EnqueueUnscannedOkPosts`.
|
||||
# Please instead update this file by running `bin/tapioca dsl Domain::Fa::EnqueueUnscannedOkPosts`.
|
||||
|
||||
|
||||
class Domain::Fa::EnqueueUnscannedOkPosts
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
|
||||
class << self
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
end
|
||||
end
|
||||
84
sorbet/rbi/dsl/domain/user.rbi
generated
84
sorbet/rbi/dsl/domain/user.rbi
generated
@@ -11,6 +11,9 @@ class Domain::User
|
||||
extend CommonRelationMethods
|
||||
extend GeneratedRelationMethods
|
||||
|
||||
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
|
||||
def favs_scan; end
|
||||
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
|
||||
@@ -457,6 +460,20 @@ class Domain::User
|
||||
sig { params(value: T::Enumerable[::Domain::Post]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids; end
|
||||
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :favs_scans`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::UserJobEvent::FavsScan::PrivateCollectionProxy) }
|
||||
def favs_scans; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::UserJobEvent::FavsScan]).void }
|
||||
def favs_scans=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids; end
|
||||
|
||||
@@ -1000,6 +1017,9 @@ class Domain::User
|
||||
sig { void }
|
||||
def restore_migrated_user_favs_at!; end
|
||||
|
||||
sig { void }
|
||||
def restore_scanned_favs_at!; end
|
||||
|
||||
sig { void }
|
||||
def restore_type!; end
|
||||
|
||||
@@ -1048,6 +1068,12 @@ class Domain::User
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_migrated_user_favs_at?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def saved_change_to_scanned_favs_at; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_scanned_favs_at?; end
|
||||
|
||||
sig { returns(T.nilable([T.untyped, T.untyped])) }
|
||||
def saved_change_to_type; end
|
||||
|
||||
@@ -1084,6 +1110,61 @@ class Domain::User
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_user_user_follows_to_count?; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at; end
|
||||
|
||||
sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def scanned_favs_at?; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_before_last_save; end
|
||||
|
||||
sig { returns(T.untyped) }
|
||||
def scanned_favs_at_before_type_cast; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def scanned_favs_at_came_from_user?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_change; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_change_to_be_saved; end
|
||||
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(::ActiveSupport::TimeWithZone),
|
||||
to: T.nilable(::ActiveSupport::TimeWithZone)
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def scanned_favs_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_in_database; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_previous_change; end
|
||||
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(::ActiveSupport::TimeWithZone),
|
||||
to: T.nilable(::ActiveSupport::TimeWithZone)
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def scanned_favs_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_previously_was; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_was; end
|
||||
|
||||
sig { void }
|
||||
def scanned_favs_at_will_change!; end
|
||||
|
||||
sig { returns(T.untyped) }
|
||||
def type; end
|
||||
|
||||
@@ -1379,6 +1460,9 @@ class Domain::User
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_migrated_user_favs_at?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_scanned_favs_at?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_type?; end
|
||||
|
||||
|
||||
14
sorbet/rbi/dsl/domain/user/e621_user.rbi
generated
14
sorbet/rbi/dsl/domain/user/e621_user.rbi
generated
@@ -510,6 +510,20 @@ class Domain::User::E621User
|
||||
sig { params(value: T::Enumerable[::Domain::Post::E621Post]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids; end
|
||||
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :favs_scans`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::UserJobEvent::FavsScan::PrivateCollectionProxy) }
|
||||
def favs_scans; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::UserJobEvent::FavsScan]).void }
|
||||
def favs_scans=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids; end
|
||||
|
||||
|
||||
14
sorbet/rbi/dsl/domain/user/fa_user.rbi
generated
14
sorbet/rbi/dsl/domain/user/fa_user.rbi
generated
@@ -549,6 +549,20 @@ class Domain::User::FaUser
|
||||
sig { params(value: T::Enumerable[::Domain::Post::FaPost]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids; end
|
||||
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :favs_scans`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::UserJobEvent::FavsScan::PrivateCollectionProxy) }
|
||||
def favs_scans; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::UserJobEvent::FavsScan]).void }
|
||||
def favs_scans=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids; end
|
||||
|
||||
|
||||
84
sorbet/rbi/dsl/domain/user/inkbunny_user.rbi
generated
84
sorbet/rbi/dsl/domain/user/inkbunny_user.rbi
generated
@@ -11,6 +11,9 @@ class Domain::User::InkbunnyUser
|
||||
extend CommonRelationMethods
|
||||
extend GeneratedRelationMethods
|
||||
|
||||
sig { returns(HasTimestampsWithDueAt::TimestampScanInfo) }
|
||||
def favs_scan; end
|
||||
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
|
||||
@@ -524,6 +527,20 @@ class Domain::User::InkbunnyUser
|
||||
sig { params(value: T::Enumerable[::Domain::Post::InkbunnyPost]).void }
|
||||
def faved_posts=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids; end
|
||||
|
||||
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
||||
def favs_scan_ids=(ids); end
|
||||
|
||||
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :favs_scans`.
|
||||
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
|
||||
sig { returns(::Domain::UserJobEvent::FavsScan::PrivateCollectionProxy) }
|
||||
def favs_scans; end
|
||||
|
||||
sig { params(value: T::Enumerable[::Domain::UserJobEvent::FavsScan]).void }
|
||||
def favs_scans=(value); end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
def followed_by_user_ids; end
|
||||
|
||||
@@ -1283,6 +1300,9 @@ class Domain::User::InkbunnyUser
|
||||
sig { void }
|
||||
def restore_name!; end
|
||||
|
||||
sig { void }
|
||||
def restore_scanned_favs_at!; end
|
||||
|
||||
sig { void }
|
||||
def restore_scanned_gallery_at!; end
|
||||
|
||||
@@ -1364,6 +1384,12 @@ class Domain::User::InkbunnyUser
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_name?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def saved_change_to_scanned_favs_at; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_scanned_favs_at?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def saved_change_to_scanned_gallery_at; end
|
||||
|
||||
@@ -1418,6 +1444,61 @@ class Domain::User::InkbunnyUser
|
||||
sig { returns(T::Boolean) }
|
||||
def saved_change_to_user_user_follows_to_count?; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at; end
|
||||
|
||||
sig { params(value: T.nilable(::ActiveSupport::TimeWithZone)).returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def scanned_favs_at?; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_before_last_save; end
|
||||
|
||||
sig { returns(T.untyped) }
|
||||
def scanned_favs_at_before_type_cast; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def scanned_favs_at_came_from_user?; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_change; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_change_to_be_saved; end
|
||||
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(::ActiveSupport::TimeWithZone),
|
||||
to: T.nilable(::ActiveSupport::TimeWithZone)
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def scanned_favs_at_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_in_database; end
|
||||
|
||||
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
|
||||
def scanned_favs_at_previous_change; end
|
||||
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(::ActiveSupport::TimeWithZone),
|
||||
to: T.nilable(::ActiveSupport::TimeWithZone)
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def scanned_favs_at_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_previously_was; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_favs_at_was; end
|
||||
|
||||
sig { void }
|
||||
def scanned_favs_at_will_change!; end
|
||||
|
||||
sig { returns(T.nilable(::ActiveSupport::TimeWithZone)) }
|
||||
def scanned_gallery_at; end
|
||||
|
||||
@@ -1870,6 +1951,9 @@ class Domain::User::InkbunnyUser
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_name?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_scanned_favs_at?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def will_save_change_to_scanned_gallery_at?; end
|
||||
|
||||
|
||||
37
sorbet/rbi/dsl/domain/user_job_event.rbi
generated
Normal file
37
sorbet/rbi/dsl/domain/user_job_event.rbi
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for dynamic methods in `Domain::UserJobEvent`.
|
||||
# Please instead update this file by running `bin/tapioca dsl Domain::UserJobEvent`.
|
||||
|
||||
|
||||
class Domain::UserJobEvent
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
|
||||
class << self
|
||||
sig do
|
||||
params(
|
||||
name: Symbol,
|
||||
type: T.any(Symbol, ActiveModel::Type::Value),
|
||||
options: T.nilable(T::Hash[Symbol, T.untyped])
|
||||
).void
|
||||
end
|
||||
def attr_json(name, type, options = nil); end
|
||||
|
||||
sig do
|
||||
params(
|
||||
default_container_attribute: T.nilable(Symbol),
|
||||
bad_cast: T.nilable(Symbol),
|
||||
unknown_key: T.nilable(Symbol)
|
||||
).void
|
||||
end
|
||||
def attr_json_config(default_container_attribute: nil, bad_cast: nil, unknown_key: nil); end
|
||||
|
||||
sig { returns(T::Array[Symbol]) }
|
||||
def attr_json_registry; end
|
||||
|
||||
sig { returns(ColorLogger) }
|
||||
def logger; end
|
||||
end
|
||||
end
|
||||
1490
sorbet/rbi/dsl/domain/user_job_event/favs_scan.rbi
generated
Normal file
1490
sorbet/rbi/dsl/domain/user_job_event/favs_scan.rbi
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -203,6 +203,18 @@ describe Domain::Fa::Job::FavsJob do
|
||||
expect(user.faved_posts).to match_array(posts)
|
||||
end
|
||||
|
||||
it "creates a favs scan job event" do
|
||||
expect do perform_now(args) end.to change(
|
||||
Domain::UserJobEvent::FavsScan,
|
||||
:count,
|
||||
).by(1)
|
||||
|
||||
favs_scan = Domain::UserJobEvent::FavsScan.last
|
||||
expect(favs_scan.user).to eq(user)
|
||||
expect(favs_scan.num_favs_added).to eq(5)
|
||||
expect(favs_scan.log_entry).to eq(log_entries[0])
|
||||
end
|
||||
|
||||
it "creates missing users" do
|
||||
expect(Domain::User::FaUser.find_by(url_name: "sepulte")).to be_nil
|
||||
expect do perform_now(args) end.to change(
|
||||
@@ -400,6 +412,20 @@ describe Domain::Fa::Job::FavsJob do
|
||||
# Should have updated scanned_favs_at
|
||||
expect(user.scanned_favs_at).to be_within(1.second).of(Time.current)
|
||||
end
|
||||
|
||||
it "creates a favs scan job event" do
|
||||
expect do perform_now(args) end.to change(
|
||||
Domain::UserJobEvent::FavsScan,
|
||||
:count,
|
||||
).by(1)
|
||||
end
|
||||
|
||||
it "records the number of favs added" do
|
||||
perform_now(args)
|
||||
favs_scan = Domain::UserJobEvent::FavsScan.last
|
||||
expect(favs_scan.log_entry).to eq(log_entries[0])
|
||||
expect(favs_scan.num_favs_added).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -752,6 +752,14 @@ describe Domain::Fa::Job::UserPageJob do
|
||||
perform_now({ url_name: "angu" })
|
||||
expect(user.scanned_favs_at).to be_within(3.seconds).of(Time.current)
|
||||
end
|
||||
|
||||
it "records a favs scan" do
|
||||
perform_now({ url_name: "angu" })
|
||||
expect(user.favs_scans.count).to eq(1)
|
||||
favs_scan = user.favs_scans.first
|
||||
expect(favs_scan.log_entry).to eq(@log_entries[0])
|
||||
expect(favs_scan.num_favs_added).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "all favorites fit in the recently faved section" do
|
||||
@@ -795,6 +803,14 @@ describe Domain::Fa::Job::UserPageJob do
|
||||
expect(user.faved_posts.count).to eq(1)
|
||||
expect(user.faved_posts.map(&:fa_id)).to eq([51_355_154])
|
||||
end
|
||||
|
||||
it "records a favs scan" do
|
||||
perform_now({ url_name: "lleaued" })
|
||||
expect(user.favs_scans.count).to eq(1)
|
||||
favs_scan = user.favs_scans.first
|
||||
expect(favs_scan.log_entry).to eq(@log_entries[0])
|
||||
expect(favs_scan.num_favs_added).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "more favorites than fits in the recent faved section" do
|
||||
@@ -822,6 +838,12 @@ describe Domain::Fa::Job::UserPageJob do
|
||||
[hash_including(user:, caused_by_entry: @log_entries[0])],
|
||||
)
|
||||
end
|
||||
|
||||
it "does not record a favs scan" do
|
||||
perform_now({ url_name: "dilgear" })
|
||||
user = Domain::User::FaUser.find_by(url_name: "dilgear")
|
||||
expect(user.favs_scans.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "the user has had a favs scan in the past" do
|
||||
@@ -873,6 +895,11 @@ describe Domain::Fa::Job::UserPageJob do
|
||||
[hash_including(user:, caused_by_entry: @log_entries[0])],
|
||||
)
|
||||
end
|
||||
|
||||
it "does not record a favs scan" do
|
||||
perform_now({ user: })
|
||||
expect(user.favs_scans.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "marks scanned_favs_at as recent" do
|
||||
|
||||
Reference in New Issue
Block a user