more typing
This commit is contained in:
105
Rakefile
105
Rakefile
@@ -111,8 +111,8 @@ task migrate_to_domain: :environment do
|
||||
end
|
||||
|
||||
if only_domains.include?("fa")
|
||||
migrator.migrate_fa_users(only_user: only_user)
|
||||
migrator.migrate_fa_posts(only_user: only_user)
|
||||
# migrator.migrate_fa_users(only_user: only_user)
|
||||
# migrator.migrate_fa_posts(only_user: only_user)
|
||||
migrator.migrate_fa_users_favs(only_user: only_user)
|
||||
migrator.migrate_fa_users_followed_users(only_user: only_user)
|
||||
end
|
||||
@@ -350,3 +350,104 @@ task perform_good_jobs: :environment do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task fix_removed_fa_posts: :environment do
|
||||
colorize_state = ->(state) do
|
||||
case state
|
||||
when "ok"
|
||||
"ok".green
|
||||
when "removed"
|
||||
"removed".red
|
||||
else
|
||||
state.to_s
|
||||
end.bold
|
||||
end
|
||||
|
||||
last_fa_id = ENV["start_at"]&.to_i
|
||||
while true
|
||||
query =
|
||||
Domain::Post::FaPost
|
||||
.where(state: "removed")
|
||||
.where.not(title: nil)
|
||||
.order(fa_id: :desc)
|
||||
query = query.where(fa_id: ...last_fa_id) if last_fa_id
|
||||
post = query.first
|
||||
break unless post
|
||||
last_fa_id = post.fa_id
|
||||
|
||||
puts "[before] [post.state: #{colorize_state.call(post.state)}] [post.file.id: #{post.file&.id}] [post.id: #{post.id}] [post.fa_id: #{post.fa_id}] [post.title: #{post.title}]"
|
||||
Domain::Fa::Job::ScanPostJob.perform_now(post: post, force_scan: true)
|
||||
post.reload
|
||||
puts "[after] [post.state: #{colorize_state.call(post.state)}] [post.file.id: #{post.file&.id}] [post.id: #{post.id}] [post.fa_id: #{post.fa_id}] [post.title: #{post.title}]"
|
||||
sleep 2
|
||||
end
|
||||
rescue => e
|
||||
puts "error: #{e.message}"
|
||||
binding.pry
|
||||
end
|
||||
|
||||
task fix_fa_user_avatars: :environment do
|
||||
new_users_missing_avatar =
|
||||
Domain::User::FaUser.where.missing(:avatar).select(:url_name)
|
||||
old_users_with_avatar =
|
||||
Domain::Fa::User
|
||||
.where(url_name: new_users_missing_avatar)
|
||||
.includes(:avatar)
|
||||
.filter(&:avatar)
|
||||
|
||||
old_users_with_avatar.each do |old_user|
|
||||
old_avatar = old_user.avatar
|
||||
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
|
||||
|
||||
if old_avatar.log_entry.nil?
|
||||
puts "enqueue fresh download for #{old_user.url_name}"
|
||||
new_avatar = Domain::UserAvatar.new
|
||||
new_user.avatar = new_avatar
|
||||
new_user.save!
|
||||
Domain::Fa::Job::UserAvatarJob.perform_now(avatar: new_avatar)
|
||||
new_avatar.reload
|
||||
|
||||
binding.pry
|
||||
next
|
||||
end
|
||||
|
||||
new_avatar = Domain::UserAvatar.new
|
||||
new_avatar.log_entry_id = old_avatar.log_entry_id
|
||||
new_avatar.last_log_entry_id = old_avatar.log_entry_id
|
||||
new_avatar.url_str = old_avatar.file_url_str
|
||||
new_avatar.downloaded_at = old_avatar.log_entry&.created_at
|
||||
new_avatar.state =
|
||||
case old_avatar.state
|
||||
when "ok"
|
||||
old_avatar.log_entry_id.present? ? "ok" : "pending"
|
||||
when "file_not_found"
|
||||
new_avatar.error_message = old_avatar.state
|
||||
"file_404"
|
||||
else
|
||||
new_avatar.error_message = old_avatar.state
|
||||
"http_error"
|
||||
end
|
||||
new_user.avatar = new_avatar
|
||||
new_user.save!
|
||||
puts "migrated #{old_user.url_name}"
|
||||
rescue => e
|
||||
puts "error: #{e.message}"
|
||||
binding.pry
|
||||
end
|
||||
end
|
||||
|
||||
task run_fa_user_avatar_jobs: :environment do
|
||||
avatars =
|
||||
Domain::UserAvatar
|
||||
.where(state: "pending")
|
||||
.joins(:user)
|
||||
.where(user: { type: Domain::User::FaUser.name })
|
||||
|
||||
puts "count: #{avatars.count}"
|
||||
|
||||
avatars.each do |avatar|
|
||||
Domain::Fa::Job::UserAvatarJob.perform_now(avatar:)
|
||||
avatar.reload
|
||||
puts "perform avatar job for #{avatar.user.url_name} - #{avatar.state.bold}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,14 +76,11 @@ module Domain::Fa::PostsHelper
|
||||
nil
|
||||
end
|
||||
|
||||
valid_type =
|
||||
[URI::HTTP, URI::HTTPS, URI::Generic].any? do |klass|
|
||||
uri.is_a?(klass)
|
||||
end
|
||||
|
||||
valid_type = !uri.is_a?(URI::MailTo)
|
||||
next { node_whitelist: [node] } if uri.nil? || !valid_type
|
||||
|
||||
uri.host ||= "www.furaffinity.net"
|
||||
uri.scheme ||= "https"
|
||||
path = uri.path
|
||||
|
||||
fa_host_matcher = /^(www\.)?furaffinity\.net$/
|
||||
|
||||
@@ -116,7 +116,16 @@ module Domain::PostsHelper
|
||||
# by default, assume the host is www.furaffinity.net
|
||||
href = node["href"]&.downcase || ""
|
||||
href = "//" + href if href.match?(/^(www\.)?furaffinity\.net/)
|
||||
uri = URI.parse(href)
|
||||
uri =
|
||||
begin
|
||||
URI.parse(href)
|
||||
rescue URI::InvalidURIError
|
||||
nil
|
||||
end
|
||||
|
||||
valid_type = !uri.is_a?(URI::MailTo)
|
||||
next { node_whitelist: [node] } if uri.nil? || !valid_type
|
||||
|
||||
uri.host ||= "www.furaffinity.net"
|
||||
path = uri.path
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
)
|
||||
end
|
||||
|
||||
post.scanned_at = DateTime.current
|
||||
logger.info format_tags("finished post scan")
|
||||
ensure
|
||||
post.save! if post
|
||||
@@ -77,6 +76,7 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
return
|
||||
end
|
||||
|
||||
post.state = "ok"
|
||||
fatal_error("submission page is not logged in") unless page.logged_in?
|
||||
|
||||
unless page.probably_submission?
|
||||
@@ -93,7 +93,7 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
submission = page.submission
|
||||
|
||||
unless submission.id.to_i == post.fa_id
|
||||
raise("id mismatch: #{submission.id} != #{post.fa_id}")
|
||||
fatal_error("id mismatch: #{submission.id} != #{post.fa_id}")
|
||||
end
|
||||
|
||||
# save before any changes so post has an id for any files
|
||||
@@ -111,7 +111,7 @@ class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
|
||||
invalid: :replace,
|
||||
undef: :replace,
|
||||
)
|
||||
post.keywords = submission.keywords_array || []
|
||||
post.keywords = submission.keywords_array
|
||||
uri = Addressable::URI.parse(submission.full_res_img)
|
||||
uri.scheme = "https" if uri.scheme.blank?
|
||||
file = post.file || post.build_file
|
||||
|
||||
@@ -15,10 +15,12 @@ class Domain::Fa::Job::UserAvatarJob < Domain::Fa::Job::Base
|
||||
return
|
||||
end
|
||||
|
||||
response =
|
||||
http_client.get("https://a.furaffinity.net/0/#{user.url_name}.gif")
|
||||
avatar_url_str =
|
||||
avatar.url_str || "https://a.furaffinity.net/0/#{user.url_name}.gif"
|
||||
response = http_client.get(avatar_url_str)
|
||||
logger.push_tags(make_arg_tag(response.log_entry))
|
||||
|
||||
avatar.url_str = avatar_url_str
|
||||
avatar.last_log_entry = response.log_entry
|
||||
avatar.downloaded_at = response.log_entry.created_at
|
||||
|
||||
|
||||
@@ -130,7 +130,13 @@ class Domain::Fa::Job::UserGalleryJob < Domain::Fa::Job::Base
|
||||
total_num_new_posts_seen += listing_page_stats.new_seen
|
||||
total_num_posts_seen += listing_page_stats.total_seen
|
||||
|
||||
page.submission_folders.each { |sf| @folders.add?(sf) } if force_scan?
|
||||
if force_scan?
|
||||
page.submission_folders.each do |sf|
|
||||
@folders.add?(
|
||||
Folder.new(href: T.must(sf[:href]), title: T.must(sf[:title])),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
page_number += 1
|
||||
break if listing_page_stats.new_seen == 0 && !@go_until_end
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
class Domain::Fa::Parser::Base
|
||||
extend T::Sig
|
||||
|
||||
sig { returns(Symbol) }
|
||||
attr_reader :page_version
|
||||
|
||||
sig { params(page_version: Symbol).void }
|
||||
def initialize(page_version)
|
||||
@page_version = page_version
|
||||
end
|
||||
|
||||
sig { returns(T.noreturn) }
|
||||
def unimplemented_version!
|
||||
raise("unimplemented page version: #{page_version}")
|
||||
end
|
||||
|
||||
sig do
|
||||
type_parameters(:Elem)
|
||||
.params(
|
||||
elem: T.all(T.type_parameter(:Elem), Nokogiri::XML::Node),
|
||||
csses: T::Array[String],
|
||||
)
|
||||
.returns(T.nilable(T.type_parameter(:Elem)))
|
||||
end
|
||||
def first_matching_css(elem, csses)
|
||||
for css in csses
|
||||
e = elem.css(css).first
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
class Domain::Fa::Parser::ListedSubmissionParserHelper
|
||||
extend T::Sig
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
attr_accessor :debug
|
||||
|
||||
sig { params(elem: Nokogiri::XML::Node, page_version: Symbol).void }
|
||||
def initialize(elem, page_version)
|
||||
@new_parse_mode = !!elem.css("figcaption").first
|
||||
@new_parse_mode = T.let(!!elem.css("figcaption").first, T::Boolean)
|
||||
@elem = elem
|
||||
@page_version = page_version
|
||||
@debug = T.let(false, T::Boolean)
|
||||
|
||||
@id = T.let(nil, T.nilable(Integer))
|
||||
@artist = T.let(nil, T.nilable(String))
|
||||
@artist_user_page_path = T.let(nil, T.nilable(String))
|
||||
@title = T.let(nil, T.nilable(String))
|
||||
@view_path = T.let(nil, T.nilable(String))
|
||||
@thumb_path = T.let(nil, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Integer)) }
|
||||
def id
|
||||
@id ||= %r{/view/(\d+)}.match(view_path).try(:[], 1).try(:to_i)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def artist
|
||||
@artist ||=
|
||||
if !@new_parse_mode
|
||||
@@ -21,6 +35,7 @@ class Domain::Fa::Parser::ListedSubmissionParserHelper
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def artist_user_page_path
|
||||
@artist_user_page_path ||=
|
||||
if !@new_parse_mode
|
||||
@@ -30,10 +45,12 @@ class Domain::Fa::Parser::ListedSubmissionParserHelper
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def artist_url_name
|
||||
artist_user_page_path.split("/").last
|
||||
artist_user_page_path&.split("/")&.last
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def title
|
||||
@title ||=
|
||||
if !@new_parse_mode
|
||||
@@ -43,6 +60,7 @@ class Domain::Fa::Parser::ListedSubmissionParserHelper
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def view_path
|
||||
@view_path ||=
|
||||
if !@new_parse_mode
|
||||
@@ -52,6 +70,7 @@ class Domain::Fa::Parser::ListedSubmissionParserHelper
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def thumb_path
|
||||
@thumb_path ||=
|
||||
if !@new_parse_mode
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
require "nokogiri"
|
||||
|
||||
class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
extend T::Sig
|
||||
|
||||
# old, old before legacy
|
||||
VERSION_0 = :old_old
|
||||
# legacy version
|
||||
@@ -11,6 +13,12 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
# redux version
|
||||
VERSION_2 = :redux
|
||||
|
||||
sig do
|
||||
params(
|
||||
page_html: T.any(String, Nokogiri::HTML4::Document),
|
||||
require_logged_in: T::Boolean,
|
||||
).void
|
||||
end
|
||||
def initialize(page_html, require_logged_in: true)
|
||||
@page =
|
||||
if page_html.is_a? Nokogiri::HTML::Document
|
||||
@@ -18,9 +26,10 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
else
|
||||
phtml = page_html.delete("\u0000")
|
||||
@phtml = phtml
|
||||
Nokogiri.HTML(phtml)
|
||||
T.cast(Nokogiri.HTML(phtml), Nokogiri::HTML4::Document)
|
||||
end
|
||||
@page_version =
|
||||
|
||||
super(
|
||||
if @page.css("link[href='/themes/beta/img/favicon.ico']").first
|
||||
VERSION_2
|
||||
elsif @page.css(".submission-list section").first ||
|
||||
@@ -28,26 +37,29 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
VERSION_1
|
||||
else
|
||||
VERSION_0
|
||||
end
|
||||
end,
|
||||
)
|
||||
|
||||
if require_logged_in && !submission_not_found?
|
||||
raise Domain::Fa::Parser::NotLoggedInError unless logged_in?
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def require_logged_in!
|
||||
if !@page.css("img.loggedin_user_avatar")&.first.nil?
|
||||
raise Domain::Fa::Parser::NotLoggedInError
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def submission_not_found?
|
||||
# the username elem is never shown on a "not found" page
|
||||
return false if logged_in_user_elem
|
||||
not_found_text =
|
||||
"The submission you are trying to find is not in our database"
|
||||
|
||||
case @page_version
|
||||
!!case @page_version
|
||||
when VERSION_2
|
||||
@page.css("body .section-body")&.first&.text&.include?(not_found_text)
|
||||
else
|
||||
@@ -59,6 +71,7 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def logged_in?
|
||||
logged_in_user_elem ? true : false
|
||||
end
|
||||
@@ -74,6 +87,7 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def favorites_next_button_id
|
||||
next_regex = %r{/favorites/.+/(\d+)/next/?}
|
||||
|
||||
@@ -98,6 +112,7 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
||||
def submission_folders
|
||||
@submission_folders ||=
|
||||
@page
|
||||
@@ -107,6 +122,7 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[Nokogiri::XML::Node]) }
|
||||
def submission_elems
|
||||
@submission_elems ||=
|
||||
case @page_version
|
||||
@@ -130,7 +146,7 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
].lazy.map { |css| @page.css(css) }.reject(&:empty?).to_a.flatten
|
||||
else
|
||||
unimplemented_version!
|
||||
end
|
||||
end.to_a
|
||||
end
|
||||
|
||||
def logged_in_user
|
||||
@@ -226,17 +242,29 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
|
||||
def user_list
|
||||
@user_list ||= Domain::Fa::Parser::UserListParserHelper.user_list(@page)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def elem_after_text_match(children, regex)
|
||||
idx = elem_idx_after_text_match(children, regex)
|
||||
return nil unless idx
|
||||
|
||||
children[idx + 1]
|
||||
end
|
||||
|
||||
def elem_idx_after_text_match(children, regex)
|
||||
children.find_index { |child| child.text.match(regex) }
|
||||
|
||||
private
|
||||
|
||||
sig do
|
||||
params(
|
||||
children: T.any(Nokogiri::XML::Node, Nokogiri::XML::NodeSet),
|
||||
regex: Regexp,
|
||||
).returns(T.nilable(Nokogiri::XML::Node))
|
||||
end
|
||||
def self.elem_after_text_match(children, regex)
|
||||
idx = elem_idx_after_text_match(children, regex)
|
||||
return nil unless idx
|
||||
|
||||
children[idx + 1]
|
||||
end
|
||||
|
||||
sig do
|
||||
params(
|
||||
children: T.any(Nokogiri::XML::Node, Nokogiri::XML::NodeSet),
|
||||
regex: Regexp,
|
||||
).returns(T.nilable(Integer))
|
||||
end
|
||||
def self.elem_idx_after_text_match(children, regex)
|
||||
children.find_index { |child| child.text.match(regex) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,15 +1,42 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
VERSION_0 = Domain::Fa::Parser::Page::VERSION_0
|
||||
VERSION_1 = Domain::Fa::Parser::Page::VERSION_1
|
||||
VERSION_2 = Domain::Fa::Parser::Page::VERSION_2
|
||||
|
||||
sig do
|
||||
params(elem: Nokogiri::XML::Node, phtml: String, page_version: Symbol).void
|
||||
end
|
||||
def initialize(elem, phtml, page_version)
|
||||
@elem = elem
|
||||
@phtml = phtml
|
||||
@page_version = page_version
|
||||
|
||||
@id = T.let(nil, T.nilable(Integer))
|
||||
@small_img = T.let(nil, T.nilable(String))
|
||||
@title = T.let(nil, T.nilable(String))
|
||||
@artist = T.let(nil, T.nilable(String))
|
||||
@artist_user_page_path = T.let(nil, T.nilable(String))
|
||||
@artist_avatar_url = T.let(nil, T.nilable(String))
|
||||
@description_html = T.let(nil, T.nilable(String))
|
||||
@full_res_img = T.let(nil, T.nilable(String))
|
||||
@posted_date = T.let(nil, T.nilable(ActiveSupport::TimeWithZone))
|
||||
@rating = T.let(nil, T.nilable(Symbol))
|
||||
@category = T.let(nil, T.nilable(String))
|
||||
@theme = T.let(nil, T.nilable(String))
|
||||
@category_full_str_redux = T.let(nil, T.nilable(String))
|
||||
@species = T.let(nil, T.nilable(String))
|
||||
@gender = T.let(nil, T.nilable(String))
|
||||
@num_favorites = T.let(nil, T.nilable(Integer))
|
||||
@num_comments = T.let(nil, T.nilable(Integer))
|
||||
@num_views = T.let(nil, T.nilable(Integer))
|
||||
@resolution_str = T.let(nil, T.nilable(String))
|
||||
@keywords_array = T.let(nil, T.nilable(T::Array[String]))
|
||||
@information_elem = T.let(nil, T.nilable(Nokogiri::XML::Node))
|
||||
@stats_container_redux = T.let(nil, T.nilable(Nokogiri::XML::Node))
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def id
|
||||
# @elem.css("form[name=myform]").first['action'].split("/").last.to_i
|
||||
@id ||=
|
||||
@@ -19,10 +46,12 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def small_img
|
||||
@elem.css("#submissionImg").first["src"].strip
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def title
|
||||
# r = @elem.css(".cat").first.text.strip
|
||||
case @page_version
|
||||
@@ -35,6 +64,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def artist
|
||||
# @elem.css(".cat a").first.text.strip
|
||||
@artist ||=
|
||||
@@ -48,6 +78,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def artist_user_page_path
|
||||
@artist_user_page_path ||=
|
||||
case @page_version
|
||||
@@ -60,10 +91,12 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def artist_url_name
|
||||
artist_user_page_path.split("/").last
|
||||
T.must(artist_user_page_path.split("/").last)
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def artist_avatar_url
|
||||
@artist_avatar_url ||=
|
||||
case @page_version
|
||||
@@ -76,6 +109,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def description_html
|
||||
case @page_version
|
||||
# when VERSION_0
|
||||
@@ -89,6 +123,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def full_res_img
|
||||
case @page_version
|
||||
when VERSION_0
|
||||
@@ -113,7 +148,12 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
@posted_date ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
idx = elem_idx_after_text_match(info_children, /Posted/)
|
||||
idx =
|
||||
Domain::Fa::Parser::Page.elem_idx_after_text_match(
|
||||
info_children,
|
||||
/Posted/,
|
||||
)
|
||||
idx = T.must(idx)
|
||||
child = info_children[idx..idx + 5].find { |ic| ic.name == "span" }
|
||||
date_str = child.try(:[], "title").try(:strip)
|
||||
if date_str
|
||||
@@ -131,6 +171,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end&.in_time_zone("UTC")
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def rating
|
||||
case @page_version
|
||||
when VERSION_2
|
||||
@@ -148,11 +189,15 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def category
|
||||
@category ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Category/).text.strip
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Category/)
|
||||
&.text
|
||||
&.strip
|
||||
when VERSION_2
|
||||
category_full_str_redux&.split(" / ")&.first&.strip
|
||||
else
|
||||
@@ -160,11 +205,15 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def theme
|
||||
@theme ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Theme/).text.strip
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Theme/)
|
||||
&.text
|
||||
&.strip
|
||||
when VERSION_2
|
||||
category_full_str_redux&.split(" / ")&.last&.strip
|
||||
else
|
||||
@@ -173,6 +222,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
|
||||
# FA started combining "Category / Theme" string into one
|
||||
sig { returns(T.nilable(String)) }
|
||||
def category_full_str_redux
|
||||
@category_full_str_redux ||=
|
||||
case @page_version
|
||||
@@ -183,11 +233,15 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def species
|
||||
@species ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Species/).try(:text).try(:strip)
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Species/)
|
||||
&.text
|
||||
&.strip
|
||||
when VERSION_2
|
||||
info_text_value_redux("Species")
|
||||
else
|
||||
@@ -195,11 +249,15 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def gender
|
||||
@gender ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Gender/).try(:text).try(:strip)
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Gender/)
|
||||
&.text
|
||||
&.strip
|
||||
when VERSION_2
|
||||
info_text_value_redux("Gender")
|
||||
else
|
||||
@@ -207,11 +265,16 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_favorites
|
||||
@num_favorites ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Favorites/).text.strip.to_i
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Favorites/)
|
||||
&.text
|
||||
&.strip
|
||||
&.to_i
|
||||
when VERSION_2
|
||||
stats_container_redux
|
||||
.css(".favorites .font-large")
|
||||
@@ -224,11 +287,16 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_comments
|
||||
@num_comments ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Comments/).text.strip.to_i
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Comments/)
|
||||
&.text
|
||||
&.strip
|
||||
&.to_i
|
||||
when VERSION_2
|
||||
stats_container_redux.css(".comments .font-large").first.text.strip.to_i
|
||||
else
|
||||
@@ -236,11 +304,16 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_views
|
||||
@num_views ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(info_children, /Views/).text.strip.to_i
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Views/)
|
||||
&.text
|
||||
&.strip
|
||||
&.to_i
|
||||
when VERSION_2
|
||||
stats_container_redux.css(".views .font-large").first.text.strip.to_i
|
||||
else
|
||||
@@ -248,24 +321,32 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def resolution_str
|
||||
@resolution_str ||=
|
||||
case @page_version
|
||||
when VERSION_0
|
||||
elem_after_text_match(info_children, /Resolution/).try(:text).try(
|
||||
:strip,
|
||||
)
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(info_children, /Resolution/)
|
||||
&.text
|
||||
&.strip
|
||||
when VERSION_1
|
||||
idx = elem_idx_after_text_match(info_children, /Resolution/)
|
||||
idx =
|
||||
Domain::Fa::Parser::Page.elem_idx_after_text_match(
|
||||
info_children,
|
||||
/Resolution/,
|
||||
)
|
||||
idx = T.must(idx)
|
||||
info_children[idx + 1].try(:text).try(:strip)
|
||||
when VERSION_2
|
||||
parts = info_text_value_redux("Size").split(" ")
|
||||
parts.first + "x" + parts.last
|
||||
parts = T.must(info_text_value_redux("Size")&.split(" "))
|
||||
"#{parts.first}x#{parts.last}"
|
||||
else
|
||||
unimplemented_version!
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def keywords_array
|
||||
@keywords_array ||=
|
||||
case @page_version
|
||||
@@ -275,19 +356,22 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
@elem.css(".tags-row .tags a").map(&:text).map(&:strip)
|
||||
else
|
||||
unimplemented_version!
|
||||
end&.reject(&:empty?)
|
||||
end&.reject(&:empty?) || []
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(Nokogiri::XML::NodeSet) }
|
||||
def info_children
|
||||
information_elem.children
|
||||
end
|
||||
|
||||
sig { params(i: Integer).returns(Nokogiri::XML::Node) }
|
||||
def info_child(i)
|
||||
information_elem.children[i]
|
||||
end
|
||||
|
||||
sig { returns(Nokogiri::XML::Node) }
|
||||
def information_elem
|
||||
@information_elem ||=
|
||||
case @page_version
|
||||
@@ -300,10 +384,12 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Nokogiri::XML::Node) }
|
||||
def info_text_elem_redux
|
||||
@elem.css("section.info.text").first
|
||||
end
|
||||
|
||||
sig { params(info_section: String).returns(T.nilable(String)) }
|
||||
def info_text_value_redux(info_section)
|
||||
info_text_elem_redux
|
||||
.css(".highlight")
|
||||
@@ -315,6 +401,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
&.strip
|
||||
end
|
||||
|
||||
sig { returns(Nokogiri::XML::NodeSet) }
|
||||
def stats_container_redux
|
||||
@elem.css(".stats-container.text")
|
||||
end
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
class Domain::Fa::Parser::UserListParserHelper
|
||||
extend T::Sig
|
||||
|
||||
User = Struct.new(:name, :url_name, :href, keyword_init: true)
|
||||
|
||||
sig { params(page: Nokogiri::XML::Node).returns(T::Array[User]) }
|
||||
def self.user_list(page)
|
||||
page
|
||||
.css(".watch-list .watch-list-items")
|
||||
@@ -10,6 +13,7 @@ class Domain::Fa::Parser::UserListParserHelper
|
||||
|
||||
private
|
||||
|
||||
sig { params(elem: Nokogiri::XML::Node).returns(User) }
|
||||
def self.watch_list_item_to_user_struct(elem)
|
||||
link = elem.css("a").first
|
||||
href = link["href"]
|
||||
|
||||
@@ -1,10 +1,33 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
extend T::Sig
|
||||
|
||||
VERSION_0 = Domain::Fa::Parser::Page::VERSION_0
|
||||
VERSION_1 = Domain::Fa::Parser::Page::VERSION_1
|
||||
VERSION_2 = Domain::Fa::Parser::Page::VERSION_2
|
||||
|
||||
sig { params(elem: Nokogiri::HTML::Document, page_version: Symbol).void }
|
||||
def initialize(elem, page_version)
|
||||
@name = T.let(nil, T.nilable(String))
|
||||
@account_status = T.let(nil, T.nilable(Symbol))
|
||||
@full_name = T.let(nil, T.nilable(String))
|
||||
@artist_type = T.let(nil, T.nilable(String))
|
||||
@profile_thumb_url = T.let(nil, T.nilable(String))
|
||||
@registered_since = T.let(nil, T.nilable(ActiveSupport::TimeWithZone))
|
||||
@mood = T.let(nil, T.nilable(String))
|
||||
@profile_html = T.let(nil, T.nilable(String))
|
||||
@num_pageviews = T.let(nil, T.nilable(Integer))
|
||||
@num_submissions = T.let(nil, T.nilable(Integer))
|
||||
@num_comments_recieved = T.let(nil, T.nilable(Integer))
|
||||
@num_comments_given = T.let(nil, T.nilable(Integer))
|
||||
@num_journals = T.let(nil, T.nilable(Integer))
|
||||
@num_favorites = T.let(nil, T.nilable(Integer))
|
||||
@recent_favs = T.let(nil, T.nilable(T::Array[Integer]))
|
||||
@recent_watchers = T.let(nil, T.nilable(T::Array[RecentUser]))
|
||||
@recent_watching = T.let(nil, T.nilable(T::Array[RecentUser]))
|
||||
@statistics = T.let(nil, T.nilable(Nokogiri::XML::Element))
|
||||
@main_about = T.let(nil, T.nilable(Nokogiri::XML::Element))
|
||||
|
||||
@elem = elem
|
||||
@page_version = page_version
|
||||
end
|
||||
@@ -18,6 +41,7 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
"∞", # deceased
|
||||
]
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def name
|
||||
@name ||=
|
||||
begin
|
||||
@@ -55,6 +79,7 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Symbol)) }
|
||||
def account_status
|
||||
@account_status ||=
|
||||
begin
|
||||
@@ -86,15 +111,25 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def full_name
|
||||
@full_name ||= elem_after_text_match(main_about.children, /Full/).text.strip
|
||||
@full_name ||=
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(main_about.children, /Full/)
|
||||
&.text
|
||||
&.strip
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def artist_type
|
||||
@artist_type ||=
|
||||
elem_after_text_match(main_about.children, /Type/).try(:text).try(:strip)
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(main_about.children, /Type/)
|
||||
&.text
|
||||
&.strip
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def profile_thumb_url
|
||||
@profile_thumb_url ||=
|
||||
case @page_version
|
||||
@@ -107,11 +142,17 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(ActiveSupport::TimeWithZone)) }
|
||||
def registered_since
|
||||
@registered_since ||=
|
||||
case @page_version
|
||||
when VERSION_0, VERSION_1
|
||||
elem_after_text_match(main_about.children, /Registered/).text.strip
|
||||
Time.zone.parse(
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(main_about.children, /Registered/)
|
||||
&.text
|
||||
&.strip,
|
||||
)
|
||||
when VERSION_2
|
||||
date_str =
|
||||
@elem
|
||||
@@ -120,40 +161,52 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
&.next_sibling
|
||||
&.text
|
||||
&.strip
|
||||
DateTime.parse(date_str) if date_str
|
||||
Time.zone.parse(date_str) if date_str
|
||||
else
|
||||
unimplemented_version!
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def mood
|
||||
@mood ||= elem_after_text_match(main_about.children, /mood/).text.strip
|
||||
@mood ||=
|
||||
Domain::Fa::Parser::Page
|
||||
.elem_after_text_match(main_about.children, /mood/)
|
||||
&.text
|
||||
&.strip
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def profile_html
|
||||
@profile_html ||= main_about.inner_html.force_encoding("utf-8")
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_pageviews
|
||||
@num_pageviews ||= stat_value(:pvs, 0)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_submissions
|
||||
@num_submissions ||= stat_value(:subs, 1)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_comments_recieved
|
||||
@num_comments_recieved ||= stat_value(:crec, 3)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_comments_given
|
||||
@num_comments_given ||= stat_value(:cgiv, 4)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_journals
|
||||
@num_journals ||= stat_value(:njr, 5)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def num_favorites
|
||||
@num_favorites ||= stat_value(:nfav, 2)
|
||||
end
|
||||
@@ -226,6 +279,7 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(legacy_name: Symbol, redux_idx: Integer).returns(Integer) }
|
||||
def stat_value(legacy_name, redux_idx)
|
||||
legacy_map = # if false # old mode?
|
||||
# { pvs: 2, subs: 5, crec: 8, cgiv: 11, njr: 14, nfav: 17 }
|
||||
@@ -251,6 +305,7 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Nokogiri::XML::Element) }
|
||||
def statistics
|
||||
@statistics ||=
|
||||
case @page_version
|
||||
@@ -267,6 +322,7 @@ class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Nokogiri::XML::Element) }
|
||||
def main_about
|
||||
@main_about ||=
|
||||
case @page_version
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
# typed: false
|
||||
# typed: strict
|
||||
class Domain::Fa::UserEnqueuer
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
|
||||
include HasBulkEnqueueJobs
|
||||
include HasColorLogger
|
||||
include HasMeasureDuration
|
||||
include Domain::Fa::HasCountFailedInQueue
|
||||
|
||||
sig do
|
||||
params(
|
||||
start_at: Integer,
|
||||
low_water_mark: Integer,
|
||||
high_water_mark: Integer,
|
||||
).void
|
||||
end
|
||||
def initialize(start_at:, low_water_mark:, high_water_mark:)
|
||||
@low_water_mark = low_water_mark
|
||||
@high_water_mark = high_water_mark
|
||||
raise if @high_water_mark <= @low_water_mark
|
||||
@user_iterator =
|
||||
Enumerator.new do |e|
|
||||
Domain::Fa::User
|
||||
.where("id >= ?", start_at)
|
||||
.find_each { |user| e << user }
|
||||
end
|
||||
T.let(
|
||||
Enumerator.new do |e|
|
||||
Domain::User::FaUser.find_each(start: start_at) { |user| e << user }
|
||||
end,
|
||||
T::Enumerator[Domain::User::FaUser],
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Symbol)) }
|
||||
def run_once
|
||||
already_enqueued = enqueued_count
|
||||
if already_enqueued <= @low_water_mark
|
||||
@@ -25,12 +37,9 @@ class Domain::Fa::UserEnqueuer
|
||||
"enqueuing #{to_enqueue.to_s.bold} more users - #{already_enqueued.to_s.bold} already enqueued",
|
||||
)
|
||||
rows =
|
||||
measure(
|
||||
proc do |p|
|
||||
p && "gathered #{p.length.to_s.bold} users to enqueue" ||
|
||||
"gathering users..."
|
||||
end,
|
||||
) { to_enqueue.times.map { @user_iterator.next } }
|
||||
measure("gathered #{to_enqueue.to_s.bold} users to enqueue") do
|
||||
to_enqueue.times.map { @user_iterator.next }
|
||||
end
|
||||
measure("enqueue jobs") do
|
||||
rows.each do |user|
|
||||
types = []
|
||||
@@ -60,14 +69,14 @@ class Domain::Fa::UserEnqueuer
|
||||
end
|
||||
end
|
||||
|
||||
avatar = user.ensure_avatar!
|
||||
if avatar.file.nil? && avatar.state == "ok"
|
||||
Domain::Fa::Job::UserAvatarJob.perform_later({ user: user })
|
||||
avatar = user.avatar || user.create_avatar!
|
||||
if avatar.log_entry.nil? && avatar.state_pending?
|
||||
Domain::Fa::Job::UserAvatarJob.perform_later({ avatar: avatar })
|
||||
types << "avatar"
|
||||
end
|
||||
|
||||
types = types.map { |t| t.bold }.join("|")
|
||||
logger.info "#{types} - #{user.url_name.bold} - #{user.id.to_s.bold}"
|
||||
logger.info "#{types} - #{user.url_name&.bold} - #{user.id.to_s.bold}"
|
||||
end
|
||||
end
|
||||
throw StopIteration if rows.empty?
|
||||
@@ -82,6 +91,7 @@ class Domain::Fa::UserEnqueuer
|
||||
|
||||
private
|
||||
|
||||
sig { returns(Integer) }
|
||||
def enqueued_count
|
||||
if Rails.env.test?
|
||||
return SpecUtil.enqueued_jobs(Domain::Fa::Job::UserFollowsJob).count
|
||||
|
||||
@@ -251,9 +251,11 @@ class Domain::MigrateToDomain
|
||||
format: "%t: %c/%C %B %p%% %a %e",
|
||||
output: @pb_sink,
|
||||
)
|
||||
query.find_each do |user|
|
||||
ReduxApplicationRecord.transaction { migrate_fa_user_favs(user) }
|
||||
pb.progress = [pb.progress + 1, pb.total].min
|
||||
query.find_in_batches(batch_size: 5) do |batch|
|
||||
ReduxApplicationRecord.transaction do
|
||||
batch.each { |user| migrate_fa_user_favs(user) }
|
||||
end
|
||||
pb.progress = [pb.progress + batch.size, pb.total].min
|
||||
end
|
||||
end
|
||||
|
||||
@@ -744,15 +746,19 @@ class Domain::MigrateToDomain
|
||||
def migrate_fa_user_favs(user)
|
||||
user_url_name = user.url_name
|
||||
old_user = Domain::Fa::User.find_by!(url_name: user_url_name)
|
||||
old_post_fa_ids = old_user.fav_posts.pluck(:fa_id)
|
||||
new_post_ids = Domain::Post::FaPost.where(fa_id: old_post_fa_ids).pluck(:id)
|
||||
|
||||
new_post_ids.each_slice(10_000) do |post_ids|
|
||||
Domain::UserPostFav.upsert_all(
|
||||
post_ids.map { |post_id| { user_id: user.id, post_id: } },
|
||||
unique_by: %i[user_id post_id],
|
||||
)
|
||||
end
|
||||
Domain::UserPostFav.connection.execute(<<~SQL)
|
||||
INSERT INTO domain_user_post_favs (user_id, post_id)
|
||||
SELECT #{user.id}, domain_posts.id
|
||||
FROM domain_fa_posts old_posts
|
||||
INNER JOIN domain_posts ON
|
||||
(domain_posts.json_attributes->>'fa_id')::integer = old_posts.fa_id
|
||||
AND domain_posts.type = 'Domain::Post::FaPost'
|
||||
INNER JOIN domain_fa_favs ON
|
||||
domain_fa_favs.post_id = old_posts.id
|
||||
AND domain_fa_favs.user_id = #{old_user.id}
|
||||
ON CONFLICT (user_id, post_id) DO NOTHING
|
||||
SQL
|
||||
|
||||
if user.faved_posts.count != old_user.fav_posts.count
|
||||
logger.error(
|
||||
|
||||
@@ -76,9 +76,9 @@ class FaBackfillFavs
|
||||
favs = T.must(user_favs[url_name])
|
||||
|
||||
page.submissions_parsed.each do |submission|
|
||||
next unless submission.id
|
||||
|
||||
favs.add(submission.id)
|
||||
fa_id = submission.id
|
||||
next unless fa_id
|
||||
favs.add(fa_id)
|
||||
end
|
||||
|
||||
break if @limit && @total_log_entries_processed >= @limit
|
||||
|
||||
@@ -20,6 +20,21 @@ module HasCompositeToParam
|
||||
sig { abstract.returns([String, Symbol]) }
|
||||
def param_prefix_and_attribute
|
||||
end
|
||||
|
||||
sig(:final) { returns(String) }
|
||||
def param_prefix
|
||||
param_prefix_and_attribute[0]
|
||||
end
|
||||
|
||||
sig(:final) { returns(Symbol) }
|
||||
def param_attribute
|
||||
param_prefix_and_attribute[1]
|
||||
end
|
||||
|
||||
sig { overridable.returns(Symbol) }
|
||||
def param_order_attribute
|
||||
param_attribute
|
||||
end
|
||||
end
|
||||
|
||||
mixes_in_class_methods(ClassMethods)
|
||||
|
||||
@@ -140,6 +140,11 @@ class Domain::Post < ReduxApplicationRecord
|
||||
def title
|
||||
end
|
||||
|
||||
sig { overridable.returns(String) }
|
||||
def title_for_view
|
||||
title || "(unknown)"
|
||||
end
|
||||
|
||||
sig { abstract.returns(T.nilable(T.any(String, Integer))) }
|
||||
def domain_id_for_view
|
||||
end
|
||||
|
||||
@@ -32,6 +32,14 @@ class Domain::Post::FaPost < Domain::Post
|
||||
|
||||
after_initialize { self.state ||= "ok" }
|
||||
|
||||
enum :state,
|
||||
{
|
||||
ok: "ok",
|
||||
removed: "removed",
|
||||
scan_error: "scan_error",
|
||||
file_error: "file_error",
|
||||
},
|
||||
prefix: "state"
|
||||
validates :state, inclusion: { in: %w[ok removed scan_error file_error] }
|
||||
validates :fa_id, presence: true
|
||||
|
||||
@@ -47,7 +55,7 @@ class Domain::Post::FaPost < Domain::Post
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
def title
|
||||
super || "(unknown)"
|
||||
super
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(Domain::User)) }
|
||||
|
||||
@@ -22,12 +22,13 @@ class Domain::PostFile < ReduxApplicationRecord
|
||||
ok: "ok",
|
||||
retryable_error: "retryable_error",
|
||||
terminal_error: "terminal_error",
|
||||
removed: "removed",
|
||||
},
|
||||
prefix: "state"
|
||||
|
||||
validates :state,
|
||||
inclusion: {
|
||||
in: %w[pending ok retryable_error terminal_error],
|
||||
in: %w[pending ok removed retryable_error terminal_error],
|
||||
}
|
||||
|
||||
after_initialize do
|
||||
|
||||
@@ -107,6 +107,7 @@ class Domain::User < ReduxApplicationRecord
|
||||
def self.has_created_posts!(klass)
|
||||
self.class_has_created_posts = klass
|
||||
has_many :posts,
|
||||
-> { order(klass.param_order_attribute => :desc) },
|
||||
through: :user_post_creations,
|
||||
source: :post,
|
||||
class_name: klass.name
|
||||
@@ -116,6 +117,7 @@ class Domain::User < ReduxApplicationRecord
|
||||
def self.has_faved_posts!(klass)
|
||||
self.class_has_faved_posts = klass
|
||||
has_many :faved_posts,
|
||||
-> { order(klass.param_order_attribute => :desc) },
|
||||
through: :user_post_favs,
|
||||
source: :post,
|
||||
class_name: klass.name
|
||||
|
||||
@@ -126,7 +126,7 @@ class Domain::User::FaUser < Domain::User
|
||||
(contents = response.contents)
|
||||
parser =
|
||||
Domain::Fa::Parser::Page.new(contents, require_logged_in: false)
|
||||
parser.user_page.account_status if parser.probably_user_page?
|
||||
parser.user_page.account_status.to_s if parser.probably_user_page?
|
||||
end
|
||||
end || "unknown"
|
||||
end
|
||||
|
||||
@@ -14,6 +14,15 @@ class Domain::UserAvatar < ReduxApplicationRecord
|
||||
belongs_to :last_log_entry, class_name: "::HttpLogEntry", optional: true
|
||||
belongs_to :log_entry, class_name: "::HttpLogEntry", optional: true
|
||||
|
||||
enum :state,
|
||||
{
|
||||
pending: "pending",
|
||||
ok: "ok",
|
||||
file_404: "file_404",
|
||||
http_error: "http_error",
|
||||
},
|
||||
prefix: "state"
|
||||
|
||||
validates :state,
|
||||
presence: true,
|
||||
inclusion: {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
),
|
||||
class:
|
||||
"max-h-[300px] max-w-[300px] rounded-md border border-slate-300 object-contain shadow-md",
|
||||
alt: post.title %>
|
||||
alt: post.title_for_view %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span>No file available</span>
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
<div class="border-t border-slate-300">
|
||||
<h2 class="p-4 text-center text-lg">
|
||||
<%= link_to post.title, domain_post_path(post), class: "sky-link" %>
|
||||
<%= link_to post.title_for_view, domain_post_path(post), class: "sky-link" %>
|
||||
</h2>
|
||||
<div class="px-4 pb-4 text-sm text-slate-500">
|
||||
<div class="flex justify-between gap-2">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="flex min-w-0 items-center gap-1">
|
||||
<%= image_tag domain_post_domain_icon_path(post), class: "w-6 h-6" %>
|
||||
<span class="truncate text-lg font-medium">
|
||||
<%= link_to post.title,
|
||||
<%= link_to post.title_for_view,
|
||||
post.domain_url_for_view.to_s,
|
||||
class: "text-blue-600 hover:underline",
|
||||
target: "_blank" %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<%= link_to "#{user.posts.count} total", domain_user_posts_path(user), class: "sky-link" %>
|
||||
</span>
|
||||
</h2>
|
||||
<% recent_posts = user.posts.order(user.class.post_order_attribute => :desc).limit(5).to_a %>
|
||||
<% recent_posts = user.posts.limit(5).to_a %>
|
||||
<% if recent_posts.any? %>
|
||||
<% recent_posts.each do |post| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<% fav_posts = user.faved_posts.limit(5).to_a %>
|
||||
<%# annoying postgres optimizer bug that causes an extremly bad plan if the limit is below 50 %>
|
||||
<% fav_posts = user.faved_posts.includes(:creator).limit(60).to_a[..5] %>
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">
|
||||
<span class="font-medium text-slate-900">Favorited Posts</span>
|
||||
|
||||
94
sorbet/rbi/dsl/domain/post/fa_post.rbi
generated
94
sorbet/rbi/dsl/domain/post/fa_post.rbi
generated
@@ -8,6 +8,7 @@
|
||||
class Domain::Post::FaPost
|
||||
include GeneratedAssociationMethods
|
||||
include GeneratedAttributeMethods
|
||||
include EnumMethodsModule
|
||||
extend CommonRelationMethods
|
||||
extend GeneratedRelationMethods
|
||||
|
||||
@@ -51,6 +52,9 @@ class Domain::Post::FaPost
|
||||
).returns(::Domain::Post::FaPost)
|
||||
end
|
||||
def new(attributes = nil, &block); end
|
||||
|
||||
sig { returns(T::Hash[T.any(String, Symbol), String]) }
|
||||
def states; end
|
||||
end
|
||||
|
||||
module CommonRelationMethods
|
||||
@@ -442,6 +446,32 @@ class Domain::Post::FaPost
|
||||
def third_to_last!; end
|
||||
end
|
||||
|
||||
module EnumMethodsModule
|
||||
sig { void }
|
||||
def state_file_error!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_file_error?; end
|
||||
|
||||
sig { void }
|
||||
def state_ok!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_ok?; end
|
||||
|
||||
sig { void }
|
||||
def state_removed!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_removed?; end
|
||||
|
||||
sig { void }
|
||||
def state_scan_error!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_scan_error?; end
|
||||
end
|
||||
|
||||
module GeneratedAssociationMethods
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::Domain::User::FaUser) }
|
||||
def build_creator(*args, &blk); end
|
||||
@@ -765,6 +795,18 @@ class Domain::Post::FaPost
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def none(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_file_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_scan_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def null_relation?(*args, &blk); end
|
||||
|
||||
@@ -824,6 +866,18 @@ class Domain::Post::FaPost
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def select(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_file_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_scan_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def strict_loading(*args, &blk); end
|
||||
|
||||
@@ -2068,7 +2122,7 @@ class Domain::Post::FaPost
|
||||
sig { returns(T.nilable(::String)) }
|
||||
def state; end
|
||||
|
||||
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
|
||||
sig { params(value: T.nilable(T.any(::String, ::Symbol))).returns(T.nilable(T.any(::String, ::Symbol))) }
|
||||
def state=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -2089,7 +2143,12 @@ class Domain::Post::FaPost
|
||||
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
|
||||
def state_change_to_be_saved; end
|
||||
|
||||
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(T.any(::String, ::Symbol)),
|
||||
to: T.nilable(T.any(::String, ::Symbol))
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def state_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::String)) }
|
||||
@@ -2098,7 +2157,12 @@ class Domain::Post::FaPost
|
||||
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
|
||||
def state_previous_change; end
|
||||
|
||||
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(T.any(::String, ::Symbol)),
|
||||
to: T.nilable(T.any(::String, ::Symbol))
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def state_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::String)) }
|
||||
@@ -2452,6 +2516,18 @@ class Domain::Post::FaPost
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def none(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_file_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_scan_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def null_relation?(*args, &blk); end
|
||||
|
||||
@@ -2511,6 +2587,18 @@ class Domain::Post::FaPost
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def select(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_file_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_scan_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def strict_loading(*args, &blk); end
|
||||
|
||||
|
||||
18
sorbet/rbi/dsl/domain/post_file.rbi
generated
18
sorbet/rbi/dsl/domain/post_file.rbi
generated
@@ -427,6 +427,12 @@ class Domain::PostFile
|
||||
sig { returns(T::Boolean) }
|
||||
def state_pending?; end
|
||||
|
||||
sig { void }
|
||||
def state_removed!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_removed?; end
|
||||
|
||||
sig { void }
|
||||
def state_retryable_error!; end
|
||||
|
||||
@@ -602,6 +608,9 @@ class Domain::PostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -673,6 +682,9 @@ class Domain::PostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -1619,6 +1631,9 @@ class Domain::PostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -1690,6 +1705,9 @@ class Domain::PostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_retryable_error(*args, &blk); end
|
||||
|
||||
|
||||
@@ -597,6 +597,9 @@ class Domain::PostFile::InkbunnyPostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -668,6 +671,9 @@ class Domain::PostFile::InkbunnyPostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -2080,6 +2086,9 @@ class Domain::PostFile::InkbunnyPostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_retryable_error(*args, &blk); end
|
||||
|
||||
@@ -2151,6 +2160,9 @@ class Domain::PostFile::InkbunnyPostFile
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_removed(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_retryable_error(*args, &blk); end
|
||||
|
||||
|
||||
94
sorbet/rbi/dsl/domain/user_avatar.rbi
generated
94
sorbet/rbi/dsl/domain/user_avatar.rbi
generated
@@ -8,6 +8,7 @@
|
||||
class Domain::UserAvatar
|
||||
include GeneratedAssociationMethods
|
||||
include GeneratedAttributeMethods
|
||||
include EnumMethodsModule
|
||||
extend CommonRelationMethods
|
||||
extend GeneratedRelationMethods
|
||||
|
||||
@@ -51,6 +52,9 @@ class Domain::UserAvatar
|
||||
).returns(::Domain::UserAvatar)
|
||||
end
|
||||
def new(attributes = nil, &block); end
|
||||
|
||||
sig { returns(T::Hash[T.any(String, Symbol), String]) }
|
||||
def states; end
|
||||
end
|
||||
|
||||
module CommonRelationMethods
|
||||
@@ -426,6 +430,32 @@ class Domain::UserAvatar
|
||||
def third_to_last!; end
|
||||
end
|
||||
|
||||
module EnumMethodsModule
|
||||
sig { void }
|
||||
def state_file_404!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_file_404?; end
|
||||
|
||||
sig { void }
|
||||
def state_http_error!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_http_error?; end
|
||||
|
||||
sig { void }
|
||||
def state_ok!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_ok?; end
|
||||
|
||||
sig { void }
|
||||
def state_pending!; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def state_pending?; end
|
||||
end
|
||||
|
||||
module GeneratedAssociationMethods
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(::HttpLogEntry) }
|
||||
def build_last_log_entry(*args, &blk); end
|
||||
@@ -582,6 +612,18 @@ class Domain::UserAvatar
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def none(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_file_404(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_http_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def null_relation?(*args, &blk); end
|
||||
|
||||
@@ -641,6 +683,18 @@ class Domain::UserAvatar
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def select(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_file_404(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_http_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) }
|
||||
def strict_loading(*args, &blk); end
|
||||
|
||||
@@ -1164,7 +1218,7 @@ class Domain::UserAvatar
|
||||
sig { returns(T.nilable(::String)) }
|
||||
def state; end
|
||||
|
||||
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
|
||||
sig { params(value: T.nilable(T.any(::String, ::Symbol))).returns(T.nilable(T.any(::String, ::Symbol))) }
|
||||
def state=(value); end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -1185,7 +1239,12 @@ class Domain::UserAvatar
|
||||
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
|
||||
def state_change_to_be_saved; end
|
||||
|
||||
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(T.any(::String, ::Symbol)),
|
||||
to: T.nilable(T.any(::String, ::Symbol))
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def state_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::String)) }
|
||||
@@ -1194,7 +1253,12 @@ class Domain::UserAvatar
|
||||
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
|
||||
def state_previous_change; end
|
||||
|
||||
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
|
||||
sig do
|
||||
params(
|
||||
from: T.nilable(T.any(::String, ::Symbol)),
|
||||
to: T.nilable(T.any(::String, ::Symbol))
|
||||
).returns(T::Boolean)
|
||||
end
|
||||
def state_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
|
||||
|
||||
sig { returns(T.nilable(::String)) }
|
||||
@@ -1461,6 +1525,18 @@ class Domain::UserAvatar
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def none(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_file_404(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_http_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def not_state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def null_relation?(*args, &blk); end
|
||||
|
||||
@@ -1520,6 +1596,18 @@ class Domain::UserAvatar
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def select(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_file_404(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_http_error(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_ok(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def state_pending(*args, &blk); end
|
||||
|
||||
sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) }
|
||||
def strict_loading(*args, &blk); end
|
||||
|
||||
|
||||
2
sorbet/rbi/dsl/good_job/jobs_controller.rbi
generated
2
sorbet/rbi/dsl/good_job/jobs_controller.rbi
generated
@@ -27,6 +27,8 @@ class GoodJob::JobsController
|
||||
include ::GoodJob::ApplicationController::HelperMethods
|
||||
include ::HelpersInterface
|
||||
include ::Domain::PostsHelper
|
||||
include ::Domain::UsersHelper
|
||||
include ::Domain::PostGroupsHelper
|
||||
include ::GoodJobHelper
|
||||
end
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ describe Domain::Fa::Job::ScanPostJob do
|
||||
post = Domain::Post::FaPost.find_by(fa_id: 59_714_213)
|
||||
expect(SpecUtil.enqueued_job_args(Domain::Fa::Job::ScanFileJob)).to match(
|
||||
array_including(
|
||||
{ post_file: post.file, caused_by_entry: @log_entries.first },
|
||||
{ file: post.file, caused_by_entry: @log_entries.first },
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
@@ -23,7 +23,7 @@ describe Domain::Inkbunny::Job::UserAvatarJob do
|
||||
end
|
||||
|
||||
def perform_job
|
||||
perform_now({ user: user })
|
||||
perform_now({ avatar: avatar })
|
||||
end
|
||||
|
||||
context "when avatar_url_str is blank" do
|
||||
@@ -159,7 +159,7 @@ describe Domain::Inkbunny::Job::UserAvatarJob do
|
||||
end
|
||||
|
||||
it "sets error state and then ok state" do
|
||||
perform_now({ user: user }, should_raise: Scraper::JobBase::JobError)
|
||||
perform_now({ avatar: avatar }, should_raise: Scraper::JobBase::JobError)
|
||||
user.reload
|
||||
avatar.reload
|
||||
|
||||
@@ -168,7 +168,7 @@ describe Domain::Inkbunny::Job::UserAvatarJob do
|
||||
expect(avatar.log_entry).to be_nil
|
||||
expect(avatar.error_message).to include("500")
|
||||
|
||||
perform_now({ user: user })
|
||||
perform_now({ avatar: avatar })
|
||||
user.reload
|
||||
avatar.reload
|
||||
|
||||
@@ -189,7 +189,10 @@ describe Domain::Inkbunny::Job::UserAvatarJob do
|
||||
let(:existing_downloaded_at) { 1.day.ago }
|
||||
|
||||
it "keeps previous file but updates state to error" do
|
||||
perform_now({ user: user }, should_raise: Scraper::JobBase::JobError)
|
||||
perform_now(
|
||||
{ avatar: avatar },
|
||||
should_raise: Scraper::JobBase::JobError,
|
||||
)
|
||||
|
||||
user.reload
|
||||
avatar.reload
|
||||
@@ -202,7 +205,7 @@ describe Domain::Inkbunny::Job::UserAvatarJob do
|
||||
existing_downloaded_at,
|
||||
)
|
||||
|
||||
perform_now({ user: user })
|
||||
perform_now({ avatar: avatar })
|
||||
user.reload
|
||||
avatar.reload
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ describe Domain::Fa::Parser::Page do
|
||||
assert_equal 6645, up.num_comments_given
|
||||
assert_equal 55, up.num_journals
|
||||
assert_equal 365_602, up.num_favorites
|
||||
assert_equal "Jan 12th, 2006 07:52", up.registered_since
|
||||
assert_equal Time.zone.parse("Jan 12th, 2006 07:52"), up.registered_since
|
||||
end
|
||||
|
||||
it "user page old old version is correct" do
|
||||
|
||||
@@ -46,7 +46,7 @@ describe Domain::Fa::Parser::Page do
|
||||
assert_equal 7_227, up.num_comments_given
|
||||
assert_equal 6, up.num_journals
|
||||
assert_equal 1_236_200, up.num_favorites
|
||||
assert_equal DateTime.new(2006, 1, 12, 7, 52), up.registered_since
|
||||
assert_equal Time.zone.parse("Jan 12th, 2006 07:52"), up.registered_since
|
||||
assert_equal "//a.furaffinity.net/1556545516/miles-df.gif",
|
||||
up.profile_thumb_url
|
||||
end
|
||||
@@ -435,7 +435,7 @@ describe Domain::Fa::Parser::Page do
|
||||
assert_page_type parser, :probably_user_page?
|
||||
up = parser.user_page
|
||||
assert_equal up.name, "MarziMoo"
|
||||
assert_equal up.registered_since, DateTime.parse("Sep 7, 2018 06:19")
|
||||
assert_equal up.registered_since, Time.zone.parse("Sep 7, 2018 06:19")
|
||||
end
|
||||
|
||||
it "handles pages with no favorites" do
|
||||
|
||||
@@ -12,10 +12,7 @@ RSpec.describe Domain::Post::FaPost do
|
||||
end
|
||||
|
||||
it "validates state inclusion" do
|
||||
post.state = "invalid"
|
||||
expect(post).not_to be_valid
|
||||
expect(post.errors[:state]).to include("is not included in the list")
|
||||
|
||||
expect do post.state = "invalid" end.to raise_error(ArgumentError)
|
||||
%w[ok removed scan_error file_error].each do |valid_state|
|
||||
post.state = valid_state
|
||||
expect(post).to be_valid
|
||||
|
||||
Reference in New Issue
Block a user