Class: Wp2txt::DumpManager
- Inherits:
-
Object
- Object
- Wp2txt::DumpManager
- Defined in:
- lib/wp2txt/multistream.rb
Overview
Manages downloading and caching of dump files Supports any Wikipedia language code (e.g., en, ja, de, fr, zh, ar, etc.) Language metadata is stored in lib/wp2txt/data/language_metadata.json
Constant Summary collapse
- DUMP_BASE_URL =
"https://dumps.wikimedia.org"- DEFAULT_CACHE_DIR =
File.("~/.wp2txt/cache")
- CACHE_DIR =
Legacy constant for backward compatibility
"tmp/dump_cache"
Instance Attribute Summary collapse
-
#cache_dir ⇒ Object
readonly
Returns the value of attribute cache_dir.
-
#dump_expiry_days ⇒ Object
readonly
Returns the value of attribute dump_expiry_days.
-
#lang ⇒ Object
readonly
Returns the value of attribute lang.
Class Method Summary collapse
-
.all_cache_status(cache_dir = DEFAULT_CACHE_DIR) ⇒ Object
Get status for all cached languages.
-
.clear_all_cache!(cache_dir = DEFAULT_CACHE_DIR) ⇒ Object
Clear all cache.
-
.default_cache_dir ⇒ Object
Get default cache directory.
Instance Method Summary collapse
-
#cache_age_days ⇒ Object
Get cache age in days Returns nil if no cache exists.
-
#cache_fresh?(days = nil) ⇒ Boolean
Check if cache is fresh (within configured days).
-
#cache_mtime ⇒ Object
Get cache modification time Returns nil if no cache exists.
-
#cache_stale? ⇒ Boolean
Check if cache is stale (beyond configured expiry days).
-
#cache_status ⇒ Object
Get cache status information.
-
#cached_index_path ⇒ Object
Get paths for cached files.
- #cached_multistream_path ⇒ Object
-
#cached_partial_multistream_path(stream_count) ⇒ Object
Path for partial multistream cache.
-
#can_resume_from_partial?(partial_info) ⇒ Hash
Check if incremental download is possible from existing partial.
-
#clear_cache! ⇒ Object
Clear cache for this language.
-
#download_index(force: false) ⇒ Object
Download multistream index file.
-
#download_multistream(force: false, max_streams: nil) ⇒ Object
Download multistream dump file.
-
#download_multistream_full(force: false, interactive: true) ⇒ String
Download full dump with incremental support.
-
#find_any_partial_cache ⇒ Hash?
Find any existing partial dump (any date).
-
#find_suitable_partial_cache(min_streams) ⇒ String?
Find a suitable cached partial download (same or larger than needed).
-
#format_size(bytes) ⇒ Object
Format bytes as human-readable string.
-
#initialize(lang, cache_dir: nil, dump_expiry_days: nil) ⇒ DumpManager
constructor
A new instance of DumpManager.
-
#latest_dump_date ⇒ Object
Get the latest dump date for a language.
Constructor Details
#initialize(lang, cache_dir: nil, dump_expiry_days: nil) ⇒ DumpManager
Returns a new instance of DumpManager.
434 435 436 437 438 439 |
# File 'lib/wp2txt/multistream.rb', line 434 def initialize(lang, cache_dir: nil, dump_expiry_days: nil) @lang = lang.to_sym @cache_dir = cache_dir || DEFAULT_CACHE_DIR @dump_expiry_days = dump_expiry_days || Wp2txt::DEFAULT_DUMP_EXPIRY_DAYS FileUtils.mkdir_p(@cache_dir) end |
Instance Attribute Details
#cache_dir ⇒ Object (readonly)
Returns the value of attribute cache_dir.
425 426 427 |
# File 'lib/wp2txt/multistream.rb', line 425 def cache_dir @cache_dir end |
#dump_expiry_days ⇒ Object (readonly)
Returns the value of attribute dump_expiry_days.
425 426 427 |
# File 'lib/wp2txt/multistream.rb', line 425 def dump_expiry_days @dump_expiry_days end |
#lang ⇒ Object (readonly)
Returns the value of attribute lang.
425 426 427 |
# File 'lib/wp2txt/multistream.rb', line 425 def lang @lang end |
Class Method Details
.all_cache_status(cache_dir = DEFAULT_CACHE_DIR) ⇒ Object
Get status for all cached languages
882 883 884 885 886 887 888 889 890 891 892 893 894 |
# File 'lib/wp2txt/multistream.rb', line 882 def self.all_cache_status(cache_dir = DEFAULT_CACHE_DIR) return {} unless File.exist?(cache_dir) status = {} Dir.glob(File.join(cache_dir, "*wiki")).each do |lang_dir| lang = File.basename(lang_dir).sub(/wiki$/, "").to_sym manager = new(lang, cache_dir: cache_dir) status[lang] = manager.cache_status rescue IOError, Errno::ENOENT, Errno::EACCES, JSON::ParserError => e status[lang] = { error: e. } end status end |
.clear_all_cache!(cache_dir = DEFAULT_CACHE_DIR) ⇒ Object
Clear all cache
877 878 879 |
# File 'lib/wp2txt/multistream.rb', line 877 def self.clear_all_cache!(cache_dir = DEFAULT_CACHE_DIR) FileUtils.rm_rf(cache_dir) if File.exist?(cache_dir) end |
.default_cache_dir ⇒ Object
Get default cache directory
429 430 431 |
# File 'lib/wp2txt/multistream.rb', line 429 def default_cache_dir DEFAULT_CACHE_DIR end |
Instance Method Details
#cache_age_days ⇒ Object
Get cache age in days Returns nil if no cache exists
838 839 840 |
# File 'lib/wp2txt/multistream.rb', line 838 def cache_age_days Wp2txt.file_age_days(cached_index_path) end |
#cache_fresh?(days = nil) ⇒ Boolean
Check if cache is fresh (within configured days)
826 827 828 829 |
# File 'lib/wp2txt/multistream.rb', line 826 def cache_fresh?(days = nil) days ||= @dump_expiry_days Wp2txt.file_fresh?(cached_index_path, days) end |
#cache_mtime ⇒ Object
Get cache modification time Returns nil if no cache exists
844 845 846 847 848 849 |
# File 'lib/wp2txt/multistream.rb', line 844 def cache_mtime path = cached_index_path return nil unless File.exist?(path) File.mtime(path) end |
#cache_stale? ⇒ Boolean
Check if cache is stale (beyond configured expiry days)
832 833 834 |
# File 'lib/wp2txt/multistream.rb', line 832 def cache_stale? !cache_fresh? end |
#cache_status ⇒ Object
Get cache status information
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 |
# File 'lib/wp2txt/multistream.rb', line 852 def cache_status { lang: @lang, cache_dir: @cache_dir, index_exists: File.exist?(cached_index_path), index_path: cached_index_path, index_size: File.exist?(cached_index_path) ? File.size(cached_index_path) : 0, multistream_exists: File.exist?(cached_multistream_path), multistream_path: cached_multistream_path, multistream_size: File.exist?(cached_multistream_path) ? File.size(cached_multistream_path) : 0, dump_date: (latest_dump_date rescue nil), fresh: cache_fresh?, age_days: cache_age_days, mtime: cache_mtime, expiry_days: @dump_expiry_days } end |
#cached_index_path ⇒ Object
Get paths for cached files
817 818 819 |
# File 'lib/wp2txt/multistream.rb', line 817 def cached_index_path File.join(@cache_dir, "#{@lang}wiki-#{latest_dump_date}-multistream-index.txt.bz2") end |
#cached_multistream_path ⇒ Object
821 822 823 |
# File 'lib/wp2txt/multistream.rb', line 821 def cached_multistream_path File.join(@cache_dir, "#{@lang}wiki-#{latest_dump_date}-multistream.xml.bz2") end |
#cached_partial_multistream_path(stream_count) ⇒ Object
Path for partial multistream cache
812 813 814 |
# File 'lib/wp2txt/multistream.rb', line 812 def cached_partial_multistream_path(stream_count) File.join(@cache_dir, "#{@lang}wiki-#{latest_dump_date}-multistream-#{stream_count}streams.xml.bz2") end |
#can_resume_from_partial?(partial_info) ⇒ Hash
Check if incremental download is possible from existing partial
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
# File 'lib/wp2txt/multistream.rb', line 564 def can_resume_from_partial?(partial_info) return { possible: false, reason: :no_partial } unless partial_info current_date = latest_dump_date # Check if dump dates match if partial_info[:dump_date] != current_date return { possible: false, reason: :date_mismatch, partial_date: partial_info[:dump_date], latest_date: current_date } end # Validate the partial file with Bz2Validator require_relative "bz2_validator" validation = Bz2Validator.validate_quick(partial_info[:path]) unless validation.valid? return { possible: false, reason: :invalid_partial, error: validation. } end # Verify file size matches expected offset index_path = download_index index = MultistreamIndex.new(index_path, cache_dir: @cache_dir) expected_size = if partial_info[:stream_count] < index.stream_offsets.size index.stream_offsets[partial_info[:stream_count]] else # Partial has all streams - no need to resume return { possible: false, reason: :already_complete } end actual_size = partial_info[:size] if actual_size != expected_size return { possible: false, reason: :size_mismatch, expected: expected_size, actual: actual_size } end { possible: true, partial_info: partial_info, current_streams: partial_info[:stream_count], total_streams: index.stream_offsets.size, current_size: actual_size } end |
#clear_cache! ⇒ Object
Clear cache for this language
871 872 873 874 |
# File 'lib/wp2txt/multistream.rb', line 871 def clear_cache! lang_dir = File.join(@cache_dir, "#{@lang}wiki") FileUtils.rm_rf(lang_dir) if File.exist?(lang_dir) end |
#download_index(force: false) ⇒ Object
Download multistream index file
452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
# File 'lib/wp2txt/multistream.rb', line 452 def download_index(force: false) index_path = cached_index_path if File.exist?(index_path) && !force puts "Index already cached: #{File.basename(index_path)}" $stdout.flush return index_path end url = index_url puts "Downloading index: #{url}" $stdout.flush download_file(url, index_path) index_path end |
#download_multistream(force: false, max_streams: nil) ⇒ Object
Download multistream dump file
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/wp2txt/multistream.rb', line 470 def download_multistream(force: false, max_streams: nil) # For partial downloads, first check if full dump exists (most efficient) if max_streams && !force full_path = cached_multistream_path if File.exist?(full_path) puts "Using cached full dump: #{File.basename(full_path)}" $stdout.flush return full_path end # Check if a larger partial download exists existing_partial = find_suitable_partial_cache(max_streams) if existing_partial puts "Using cached partial: #{File.basename(existing_partial)}" $stdout.flush return existing_partial end end dump_path = max_streams ? cached_partial_multistream_path(max_streams) : cached_multistream_path if File.exist?(dump_path) && !force puts "Multistream already cached: #{File.basename(dump_path)}" $stdout.flush return dump_path end url = multistream_url if max_streams # Partial download: need index first to know byte range index_path = download_index index = MultistreamIndex.new(index_path, cache_dir: @cache_dir) if index.stream_offsets.size >= max_streams # Get byte range for first N streams end_offset = index.stream_offsets[max_streams] puts "Downloading first #{max_streams} streams (#{format_size(end_offset)}): #{url}" $stdout.flush download_file_range(url, dump_path, 0, end_offset - 1) else puts "Only #{index.stream_offsets.size} streams available, downloading all" $stdout.flush download_file(url, dump_path) end else puts "Downloading multistream: #{url}" $stdout.flush download_file(url, dump_path) end dump_path end |
#download_multistream_full(force: false, interactive: true) ⇒ String
Download full dump with incremental support
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
# File 'lib/wp2txt/multistream.rb', line 624 def download_multistream_full(force: false, interactive: true) full_path = cached_multistream_path # If full dump exists, use it if File.exist?(full_path) && !force puts "Using cached full dump: #{File.basename(full_path)}" $stdout.flush return full_path end # Check for existing partial dump partial = find_any_partial_cache if partial && interactive resume_info = can_resume_from_partial?(partial) if resume_info[:possible] # Same date - can resume return handle_resumable_partial(partial, resume_info, force) elsif resume_info[:reason] == :date_mismatch # Different date - ask user return handle_outdated_partial(partial, resume_info, force) elsif resume_info[:reason] == :size_mismatch || resume_info[:reason] == :invalid_partial # Corrupted partial - inform and re-download puts "Warning: Existing partial dump appears corrupted." puts " Reason: #{resume_info[:reason]}" puts " Will download fresh copy." FileUtils.rm_f(partial[:path]) end end # Standard full download download_multistream(force: force, max_streams: nil) end |
#find_any_partial_cache ⇒ Hash?
Find any existing partial dump (any date)
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 |
# File 'lib/wp2txt/multistream.rb', line 539 def find_any_partial_cache pattern = File.join(@cache_dir, "#{@lang}wiki-*-multistream-*streams.xml.bz2") partials = [] Dir.glob(pattern).each do |path| if path =~ /#{@lang}wiki-(\d{8})-multistream-(\d+)streams\.xml\.bz2$/ dump_date = $1 stream_count = $2.to_i partials << { path: path, dump_date: dump_date, stream_count: stream_count, size: File.size(path), mtime: File.mtime(path) } end end # Return the largest partial (by stream count) partials.max_by { |p| p[:stream_count] } end |
#find_suitable_partial_cache(min_streams) ⇒ String?
Find a suitable cached partial download (same or larger than needed)
526 527 528 529 530 531 532 533 534 535 |
# File 'lib/wp2txt/multistream.rb', line 526 def find_suitable_partial_cache(min_streams) pattern = File.join(@cache_dir, "#{@lang}wiki-#{latest_dump_date}-multistream-*streams.xml.bz2") Dir.glob(pattern).each do |path| if path =~ /multistream-(\d+)streams\.xml\.bz2$/ stream_count = $1.to_i return path if stream_count >= min_streams end end nil end |
#format_size(bytes) ⇒ Object
Format bytes as human-readable string
442 443 444 |
# File 'lib/wp2txt/multistream.rb', line 442 def format_size(bytes) Wp2txt.format_file_size(bytes) end |
#latest_dump_date ⇒ Object
Get the latest dump date for a language
447 448 449 |
# File 'lib/wp2txt/multistream.rb', line 447 def latest_dump_date @latest_dump_date ||= fetch_latest_dump_date end |