Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 2 additions & 27 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2026-05-13 08:31:40 UTC using RuboCop version 1.86.1.
# on 2026-05-13 13:11:16 UTC using RuboCop version 1.86.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand All @@ -19,14 +19,6 @@ Layout/LineLength:
- 'lib/archaeo/url_rewriter.rb'
- 'spec/archaeo/asset_extractor_spec.rb'

# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented, indented_relative_to_receiver
Layout/MultilineMethodCallIndentation:
Exclude:
- 'lib/archaeo/page.rb'

# Offense count: 3
Lint/UselessConstantScoping:
Exclude:
Expand All @@ -46,7 +38,7 @@ Metrics/AbcSize:
- 'lib/archaeo/cli.rb'
- 'lib/archaeo/configuration.rb'
- 'lib/archaeo/content_tracker.rb'
- 'lib/archaeo/coverage_report.rb'
- 'lib/archaeo/coverage_analyzer.rb'
- 'lib/archaeo/local_rewriter.rb'
- 'lib/archaeo/parallel_cdx.rb'
- 'lib/archaeo/pattern_filter.rb'
Expand Down Expand Up @@ -114,14 +106,6 @@ Performance/StringBytesize:
Exclude:
- 'lib/archaeo/encoding_detector.rb'

# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants.
# SupportedStyles: described_class, explicit
RSpec/DescribedClass:
Exclude:
- 'spec/archaeo/page_microposts_spec.rb'

# Offense count: 131
# Configuration parameters: CountAsOne.
RSpec/ExampleLength:
Expand Down Expand Up @@ -178,12 +162,3 @@ RSpec/RepeatedExampleGroupDescription:
RSpec/StubbedMock:
Exclude:
- 'spec/archaeo/parallel_cdx_spec.rb'

# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
Style/TrailingCommaInArguments:
Exclude:
- 'lib/archaeo/cli.rb'
- 'lib/archaeo/page.rb'
3 changes: 3 additions & 0 deletions lib/archaeo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,7 @@ def initialize(message, status_code:, url:, page:)
autoload :SearchResult, "archaeo/archive_search"
autoload :LocalRewriter, "archaeo/local_rewriter"
autoload :LocalRewriteSummary, "archaeo/local_rewriter"
autoload :CoverageAnalyzer, "archaeo/coverage_analyzer"
autoload :HealthReport, "archaeo/health_report"
autoload :HealthDetail, "archaeo/health_report"
end
14 changes: 0 additions & 14 deletions lib/archaeo/archive_health_check.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
# frozen_string_literal: true

module Archaeo
# Verifies that archived snapshots are still accessible.
#
# Checks each snapshot by performing HEAD requests to the
# archive URL and reporting accessibility status.
HealthReport = Struct.new(
:total, :accessible, :missing, :errors, :details,
keyword_init: true
)

HealthDetail = Struct.new(
:snapshot, :status, :error,
keyword_init: true
)

class ArchiveHealthCheck
def initialize(client: HttpClient.new, cdx_api: nil)
@client = client
Expand Down
6 changes: 3 additions & 3 deletions lib/archaeo/bulk_downloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,6 @@ def fetch_and_save(snapshot)
page = fetch_page(snapshot)
validate_page_status(page, snapshot)
write_page_file(page, snapshot)
rescue StandardError
FileUtils.rm_f(tmp_path) if defined?(tmp_path)
raise
end

def fetch_page(snapshot)
Expand All @@ -272,6 +269,9 @@ def write_page_file(page, snapshot)
File.binwrite(tmp_path, page.content)
File.rename(tmp_path, filename)
page.content
rescue StandardError
FileUtils.rm_f(tmp_path) if defined?(tmp_path)
raise
end

EXTENSION_MAP = {
Expand Down
50 changes: 50 additions & 0 deletions lib/archaeo/coverage_analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module Archaeo
# Builds a CoverageReport from CDX snapshot data.
class CoverageAnalyzer
def initialize(cdx_api: nil)
@cdx_api = cdx_api
end

def analyze(url, from: nil, to: nil)
cdx = @cdx_api || CdxApi.new
snapshots = cdx.snapshots(url, from: from, to: to).to_a

unique_urls = snapshots.map(&:original_url).uniq
status_dist = compute_status_distribution(snapshots)
gaps = compute_temporal_gaps(snapshots)

CoverageReport.new(
url: url,
total_urls: unique_urls.size,
archived_urls: snapshots.count(&:success?),
status_distribution: status_dist,
temporal_gaps: gaps,
)
end

private

def compute_status_distribution(snapshots)
snapshots.each_with_object(Hash.new(0)) do |snap, counts|
counts[snap.status_code] += 1
end
end

def compute_temporal_gaps(snapshots)
return [] if snapshots.size < 2

sorted = snapshots.sort_by(&:timestamp)
gaps = []
sorted.each_cons(2) do |a, b|
diff_days = (b.timestamp.to_time - a.timestamp.to_time) / 86400
next unless diff_days > 30

gaps << { from: a.timestamp.to_s, to: b.timestamp.to_s,
gap_days: diff_days.round }
end
gaps
end
end
end
47 changes: 0 additions & 47 deletions lib/archaeo/coverage_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,51 +51,4 @@ def as_json(*)
to_h
end
end

# Builds a CoverageReport from CDX snapshot data.
class CoverageAnalyzer
def initialize(cdx_api: nil)
@cdx_api = cdx_api
end

def analyze(url, from: nil, to: nil)
cdx = @cdx_api || CdxApi.new
snapshots = cdx.snapshots(url, from: from, to: to).to_a

unique_urls = snapshots.map(&:original_url).uniq
status_dist = compute_status_distribution(snapshots)
gaps = compute_temporal_gaps(snapshots)

CoverageReport.new(
url: url,
total_urls: unique_urls.size,
archived_urls: snapshots.count(&:success?),
status_distribution: status_dist,
temporal_gaps: gaps,
)
end

private

def compute_status_distribution(snapshots)
snapshots.each_with_object(Hash.new(0)) do |snap, counts|
counts[snap.status_code] += 1
end
end

def compute_temporal_gaps(snapshots)
return [] if snapshots.size < 2

sorted = snapshots.sort_by(&:timestamp)
gaps = []
sorted.each_cons(2) do |a, b|
diff_days = (b.timestamp.to_time - a.timestamp.to_time) / 86400
next unless diff_days > 30

gaps << { from: a.timestamp.to_s, to: b.timestamp.to_s,
gap_days: diff_days.round }
end
gaps
end
end
end
9 changes: 3 additions & 6 deletions lib/archaeo/download_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,15 @@ def file_exists?(timestamp, base_dir: @output_dir)
def stale_entries(base_dir: @output_dir)
@mutex.synchronize do
entries.reject do |e|
find_file(base_dir,
e["ts"]) && File.exist?(find_file(base_dir, e["ts"]))
path = find_file(base_dir, e["ts"])
path && File.exist?(path)
end
end
end

def cleanup_stale(base_dir: @output_dir)
@mutex.synchronize do
stale = entries.reject do |e|
path = find_file(base_dir, e["ts"])
path && File.exist?(path)
end
stale = stale_entries(base_dir: base_dir)
@entries = entries - stale
@entries_key = nil
save
Expand Down
13 changes: 13 additions & 0 deletions lib/archaeo/health_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Archaeo
HealthReport = Struct.new(
:total, :accessible, :missing, :errors, :details,
keyword_init: true
)

HealthDetail = Struct.new(
:snapshot, :status, :error,
keyword_init: true
)
end
Loading
Loading