Class: Html2rss::Scoring::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/html2rss/scoring/engine.rb

Overview

Ranks AutoSource::Segment instances with an explicit feature registry (no send).

Constant Summary collapse

TOP_K =

Soft cap after score-floor filtering (lazy extraction guard; breadth > blind top-50).

99
SCORE_FLOOR =

Minimum composite score retained by #rank_top (precision floor).

0.0
QUALITY_RULES =

Quality feature triples: [FeatureId, predicate, weight].

[
  [:title_word_count_ge3, ->(o) { o.title_word_count >= 3 }, 40],
  [:title_word_count_ge7, ->(o) { o.title_word_count >= 7 }, 15],
  [:path_length_gt6, ->(o) { o.path_length > 6 }, 20],
  [:content_path, lambda(&:content_path), 15],
  [:publish_marker, lambda(&:publish_marker), 15],
  [:descriptive_context, lambda(&:descriptive_context), 10],
  [:article_container, lambda(&:article_container), 10],
  [:content_tokens, lambda(&:content_tokens), 10]
].freeze
JUNK_RULES =

Junk feature triples: [FeatureId, predicate, weight].

[
  [:non_content_utility_path, ->(o) { o.utility_path && !o.content_path && !o.strong_post_suffix }, 25],
  [:utility_prefix_title_short, ->(o) { o.utility_prefix_title && o.title_word_count <= 6 }, 15],
  [:shallow, lambda(&:shallow), 10],
  [:weak_container, ->(o) { !o.publish_marker && !o.descriptive_context }, 10],
  [:recommended_title_non_content, ->(o) { o.recommended_title && !o.content_path }, 10],
  [:high_confidence_junk_path, lambda(&:high_confidence_junk_path), 5],
  [:junk_tokens, lambda(&:junk_tokens), 15]
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(link_resolver:) ⇒ Engine

Returns a new instance of Engine.

Parameters:



39
40
41
42
# File 'lib/html2rss/scoring/engine.rb', line 39

def initialize(link_resolver:)
  @link_resolver = link_resolver
  @assessor = ContainerAssessor.new(text_classifier: link_resolver.text_classifier)
end

Instance Method Details

#rank(segments) ⇒ Array<RankedSegment>

Returns sorted by composite desc, position asc; hard-junk dropped.

Parameters:

Returns:

  • (Array<RankedSegment>)

    sorted by composite desc, position asc; hard-junk dropped



47
48
49
50
# File 'lib/html2rss/scoring/engine.rb', line 47

def rank(segments)
  segments.filter_map { |segment| rank_one(segment) }
          .sort_by { |ranked| [-ranked.final_score, ranked.position] }
end

#rank_top(segments, limit: TOP_K) ⇒ Array<RankedSegment>

Returns score-floor filtered, then capped.

Parameters:

Returns:



68
69
70
# File 'lib/html2rss/scoring/engine.rb', line 68

def rank_top(segments, limit: TOP_K)
  rank(segments).select { |ranked| ranked.final_score >= SCORE_FLOOR }.first(limit)
end

#select_eligible(segments, limit: TOP_K) ⇒ Array<RankedSegment>

Filters hard-junk but preserves discovery order (list/cluster scrapers).

Parameters:

Returns:



58
59
60
61
62
# File 'lib/html2rss/scoring/engine.rb', line 58

def select_eligible(segments, limit: TOP_K)
  segments.filter_map { |segment| rank_one(segment, hard_junk_mode: :lenient) }
          .sort_by(&:position)
          .first(limit)
end