Enhance PostsHelper and View Logic for Improved Post Metadata Display
- Updated `PostsHelper` to enforce strict typing and added new methods for guessing HTTP log entries related to scanned posts and file downloads. - Refactored the `post_state_string` method to handle unknown states more gracefully. - Modified the view template to replace the old scanned and file description logic with links to log entries, providing clearer metadata about post actions. - Removed deprecated tests related to the old description methods and added new tests for the updated functionality. These changes improve the clarity and usability of post metadata in the application.
This commit is contained in:
@@ -1,15 +1,29 @@
|
||||
# typed: false
|
||||
# typed: strict
|
||||
module Domain::Fa::PostsHelper
|
||||
extend T::Sig
|
||||
|
||||
include ActionView::Helpers::DateHelper
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
include ActionView::Helpers::RenderingHelper
|
||||
include ActionView::Helpers::TagHelper
|
||||
|
||||
sig { params(post: Domain::Fa::Post).returns(String) }
|
||||
def post_state_string(post)
|
||||
if post.have_file?
|
||||
"file"
|
||||
elsif post.scanned?
|
||||
"scanned"
|
||||
else
|
||||
post.state
|
||||
post.state || "unknown"
|
||||
end
|
||||
end
|
||||
|
||||
sig do
|
||||
params(
|
||||
params:
|
||||
T.any(ActionController::Parameters, T::Hash[T.untyped, T.untyped]),
|
||||
).returns(T.nilable(String))
|
||||
end
|
||||
def page_str(params)
|
||||
if (params[:page] || 1).to_i > 1
|
||||
"(page #{params[:page]})"
|
||||
@@ -18,23 +32,21 @@ module Domain::Fa::PostsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def scanned_and_file_description(post)
|
||||
parts = []
|
||||
if post.scanned?
|
||||
time_ago =
|
||||
(post.scanned_at ? time_ago_in_words(post.scanned_at) : "(unknown)")
|
||||
parts << "Scanned #{time_ago} ago"
|
||||
else
|
||||
parts << "Not scanned"
|
||||
end
|
||||
if post.file
|
||||
parts << "file #{time_ago_in_words(post.file.created_at)} ago"
|
||||
else
|
||||
parts << "no file"
|
||||
end
|
||||
parts.join(", ")
|
||||
sig { params(post: Domain::Fa::Post).returns(T.nilable(HttpLogEntry)) }
|
||||
def guess_scanned_http_log_entry(post)
|
||||
HttpLogEntry.find_all_by_uri(
|
||||
"https://www.furaffinity.net/view/#{post.fa_id}",
|
||||
).first
|
||||
end
|
||||
|
||||
sig { params(post: Domain::Fa::Post).returns(T.nilable(HttpLogEntry)) }
|
||||
def guess_file_downloaded_http_log_entry(post)
|
||||
if (uri = post.file_uri)
|
||||
HttpLogEntry.find_all_by_uri(uri).first
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(html: String).returns(String) }
|
||||
def fa_post_description_sanitized(html)
|
||||
fa_post_id_to_node = {}
|
||||
fa_user_url_name_to_node = {}
|
||||
@@ -49,7 +61,7 @@ module Domain::Fa::PostsHelper
|
||||
properties: %w[font-size color],
|
||||
},
|
||||
transformers: [
|
||||
lambda do |env|
|
||||
Kernel.lambda do |env|
|
||||
# Only allow and transform FA links
|
||||
if env[:node_name] == "a"
|
||||
node = env[:node]
|
||||
@@ -66,12 +78,12 @@ module Domain::Fa::PostsHelper
|
||||
fa_user_matcher = %r{^/user/(\w+)/?$}
|
||||
|
||||
if fa_host_matcher.match?(uri.host) && path
|
||||
if path.match?(fa_post_matcher)
|
||||
fa_id = path.match(fa_post_matcher)[1].to_i
|
||||
if match = path.match(fa_post_matcher)
|
||||
fa_id = match[1].to_i
|
||||
fa_post_id_to_node[fa_id] = node
|
||||
next { node_whitelist: [node] }
|
||||
elsif path.match?(fa_user_matcher)
|
||||
fa_url_name = path.match(fa_user_matcher)[1]
|
||||
elsif match = path.match(fa_user_matcher)
|
||||
fa_url_name = match[1]
|
||||
fa_user_url_name_to_node[fa_url_name] = node
|
||||
next { node_whitelist: [node] }
|
||||
end
|
||||
|
||||
@@ -275,7 +275,7 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
|
||||
@elem.css(".tags-row .tags a").map(&:text).map(&:strip)
|
||||
else
|
||||
unimplemented_version!
|
||||
end
|
||||
end&.reject(&:empty?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -47,8 +47,27 @@
|
||||
</span>
|
||||
</div>
|
||||
<% if policy(@post).view_scraper_metadata? %>
|
||||
<% if (scan_desc = scanned_and_file_description(@post)) %>
|
||||
<div class="mt-2 text-sm text-slate-500"><%= scan_desc %></div>
|
||||
<% if hle = guess_scanned_http_log_entry(@post) %>
|
||||
<div class="mt-2 text-sm text-slate-500">
|
||||
<%= link_to "Scanned #{time_ago_in_words(hle.requested_at)} ago",
|
||||
log_entry_path(hle),
|
||||
class: "text-blue-600 hover:underline",
|
||||
target: "_blank" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-2 text-sm text-slate-500">Unknown when post scanned</div>
|
||||
<% end %>
|
||||
<% if hle = guess_file_downloaded_http_log_entry(@post) %>
|
||||
<div class="mt-2 text-sm text-slate-500">
|
||||
<%= link_to "File downloaded #{time_ago_in_words(hle.requested_at)} ago",
|
||||
log_entry_path(hle),
|
||||
class: "text-blue-600 hover:underline",
|
||||
target: "_blank" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-2 text-sm text-slate-500">
|
||||
Unknown when file downloaded
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -86,8 +105,6 @@
|
||||
<% end %>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= render "section_description", { post: @post } %>
|
||||
<%= render "section_similar_posts", { post: @post } %>
|
||||
</div>
|
||||
|
||||
@@ -52,65 +52,6 @@ RSpec.describe Domain::Fa::PostsHelper, type: :helper do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#scanned_and_file_description" do
|
||||
let(:post) { build(:domain_fa_post) }
|
||||
let(:file) { double("file", created_at: 1.day.ago) }
|
||||
|
||||
context "when post is scanned with known time" do
|
||||
before do
|
||||
allow(post).to receive(:scanned?).and_return(true)
|
||||
allow(post).to receive(:scanned_at).and_return(2.hours.ago)
|
||||
end
|
||||
|
||||
it "includes scan time" do
|
||||
allow(post).to receive(:file).and_return(nil)
|
||||
expect(helper.scanned_and_file_description(post)).to include(
|
||||
"Scanned about 2 hours ago",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when post is scanned but time unknown" do
|
||||
before do
|
||||
allow(post).to receive(:scanned?).and_return(true)
|
||||
allow(post).to receive(:scanned_at).and_return(nil)
|
||||
end
|
||||
|
||||
it "shows unknown scan time" do
|
||||
allow(post).to receive(:file).and_return(nil)
|
||||
expect(helper.scanned_and_file_description(post)).to include(
|
||||
"Scanned (unknown) ago",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when post has file" do
|
||||
before do
|
||||
allow(post).to receive(:scanned?).and_return(false)
|
||||
allow(post).to receive(:file).and_return(file)
|
||||
end
|
||||
|
||||
it "includes file time" do
|
||||
expect(helper.scanned_and_file_description(post)).to include(
|
||||
"file 1 day ago",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when post has neither scan nor file" do
|
||||
before do
|
||||
allow(post).to receive(:scanned?).and_return(false)
|
||||
allow(post).to receive(:file).and_return(nil)
|
||||
end
|
||||
|
||||
it "shows appropriate message" do
|
||||
expect(helper.scanned_and_file_description(post)).to eq(
|
||||
"Not scanned, no file",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#fa_post_description_sanitized" do
|
||||
describe "basic HTML sanitization" do
|
||||
it "works" do
|
||||
|
||||
@@ -465,6 +465,35 @@ describe Domain::Fa::Parser::Page do
|
||||
assert_nil parser.favorites_next_button_id
|
||||
end
|
||||
|
||||
it "parses 2025 format" do
|
||||
parser = get_parser("submission_59584979_2025_style_keywords.html")
|
||||
assert_page_type parser, :probably_submission?
|
||||
sub = parser.submission
|
||||
|
||||
assert_equal 59_584_979, sub.id
|
||||
assert_equal "Felureii", sub.artist
|
||||
assert_equal "/user/felureii/", sub.artist_user_page_path
|
||||
assert_equal "//a.furaffinity.net/1733967392/felureii.gif",
|
||||
sub.artist_avatar_url
|
||||
assert_equal "Practice", sub.title
|
||||
assert_equal :general, sub.rating
|
||||
assert_equal "//d.furaffinity.net/art/felureii/1737390216/1737390216.felureii_0f3gn7ifbl8.jpg",
|
||||
sub.small_img
|
||||
assert_equal "//d.furaffinity.net/art/felureii/1737390216/1737390216.felureii_0f3gn7ifbl8.jpg",
|
||||
sub.full_res_img
|
||||
assert_equal Time.zone.parse("Jan 20, 2025 11:23"), sub.posted_date
|
||||
assert_equal "Artwork (Digital)", sub.category
|
||||
assert_equal "All", sub.theme
|
||||
assert_equal "Unspecified / Any", sub.species
|
||||
assert_equal "Any", sub.gender
|
||||
assert_equal 0, sub.num_favorites
|
||||
assert_equal 0, sub.num_comments
|
||||
assert_equal 16, sub.num_views
|
||||
assert_equal "677x672", sub.resolution_str
|
||||
assert_equal %w[art troll fantasy], sub.keywords_array
|
||||
assert_equal "Nothing special, just practice.", sub.description_html.strip
|
||||
end
|
||||
|
||||
def get_parser(file, require_logged_in: true)
|
||||
path = File.join("domain/fa/parser/redux", file)
|
||||
contents =
|
||||
|
||||
924
test/fixtures/files/domain/fa/parser/redux/submission_59584979_2025_style_keywords.html
vendored
Normal file
924
test/fixtures/files/domain/fa/parser/redux/submission_59584979_2025_style_keywords.html
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user