wp2txt 2.3.2 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Benchmark script for wp2txt regex performance
4
- # Compares pre-compiled regex patterns vs inline compilation
5
- #
6
- # Usage: ruby scripts/benchmark_regex.rb
7
-
8
- require "benchmark"
9
- begin
10
- require "benchmark/ips"
11
- rescue LoadError
12
- # benchmark-ips is optional
13
- end
14
-
15
- # Add lib to load path
16
- $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
17
- require "wp2txt"
18
- require "wp2txt/article"
19
-
20
- # Sample Wikipedia-like content for benchmarking
21
- SAMPLE_TEXT = <<~WIKI
22
- {{Infobox person
23
- | name = Test Person
24
- | birth_date = 1980-01-01
25
- | occupation = Writer
26
- }}
27
- '''Test Article''' is a [[test]] article with various [[wiki markup|markup]].
28
-
29
- == Section 1 ==
30
- This section has some {{cite web|url=http://example.com|title=Example}} references.
31
- There are also [[Category:Test]] links and [[File:Image.jpg|thumb|A caption]].
32
-
33
- === Subsection ===
34
- More content with '''bold''' and ''italic'' text.
35
- * List item 1
36
- * List item 2
37
- # Numbered item
38
-
39
- == Section 2 ==
40
- {| class="wikitable"
41
- |-
42
- ! Header 1 !! Header 2
43
- |-
44
- | Cell 1 || Cell 2
45
- |}
46
-
47
- Some text with &nbsp; entities and &#x266A; characters.
48
- Also has <ref name="test">Reference content</ref> and <nowiki>[[preserved]]</nowiki>.
49
-
50
- {{DEFAULTSORT:Test Article}}
51
- [[Category:Articles]]
52
- [[Category:Tests]]
53
- WIKI
54
-
55
- # Create multiple copies for more realistic benchmarking
56
- LARGE_TEXT = (SAMPLE_TEXT * 100).freeze
57
-
58
- class BenchmarkRunner
59
- include Wp2txt
60
-
61
- def initialize
62
- @nowikis = {}
63
- end
64
-
65
- def run_cleanup(text)
66
- cleanup(text.dup)
67
- end
68
-
69
- def run_full_format(text)
70
- format_wiki(text.dup)
71
- end
72
- end
73
-
74
- def run_benchmarks
75
- puts "=" * 60
76
- puts "wp2txt Regex Performance Benchmark"
77
- puts "=" * 60
78
- puts
79
- puts "Ruby version: #{RUBY_VERSION}"
80
- puts "Sample text size: #{SAMPLE_TEXT.bytesize} bytes"
81
- puts "Large text size: #{LARGE_TEXT.bytesize} bytes"
82
- puts
83
-
84
- runner = BenchmarkRunner.new
85
-
86
- puts "-" * 60
87
- puts "Warmup (JIT compilation, method caching)"
88
- puts "-" * 60
89
- 5.times { runner.run_cleanup(SAMPLE_TEXT) }
90
- 5.times { runner.run_full_format(SAMPLE_TEXT) }
91
- puts "Done."
92
- puts
93
-
94
- puts "-" * 60
95
- puts "Benchmark: cleanup() method"
96
- puts "-" * 60
97
-
98
- Benchmark.bm(20) do |x|
99
- x.report("cleanup (small):") do
100
- 1000.times { runner.run_cleanup(SAMPLE_TEXT) }
101
- end
102
-
103
- x.report("cleanup (large):") do
104
- 10.times { runner.run_cleanup(LARGE_TEXT) }
105
- end
106
- end
107
-
108
- puts
109
- puts "-" * 60
110
- puts "Benchmark: format_wiki() method (full pipeline)"
111
- puts "-" * 60
112
-
113
- Benchmark.bm(20) do |x|
114
- x.report("format_wiki (small):") do
115
- 1000.times { runner.run_full_format(SAMPLE_TEXT) }
116
- end
117
-
118
- x.report("format_wiki (large):") do
119
- 10.times { runner.run_full_format(LARGE_TEXT) }
120
- end
121
- end
122
-
123
- # If benchmark-ips is available, run IPS benchmarks
124
- if defined?(Benchmark::IPS)
125
- puts
126
- puts "-" * 60
127
- puts "IPS Benchmark (iterations per second)"
128
- puts "-" * 60
129
-
130
- Benchmark.ips do |x|
131
- x.report("cleanup") { runner.run_cleanup(SAMPLE_TEXT) }
132
- x.report("format_wiki") { runner.run_full_format(SAMPLE_TEXT) }
133
- x.compare!
134
- end
135
- end
136
-
137
- puts
138
- puts "-" * 60
139
- puts "Memory profile (approximate)"
140
- puts "-" * 60
141
-
142
- # Simple memory measurement
143
- GC.start
144
- before = GC.stat[:total_allocated_objects]
145
- 100.times { runner.run_cleanup(SAMPLE_TEXT) }
146
- after = GC.stat[:total_allocated_objects]
147
- puts "cleanup() allocations per call: ~#{(after - before) / 100}"
148
-
149
- GC.start
150
- before = GC.stat[:total_allocated_objects]
151
- 100.times { runner.run_full_format(SAMPLE_TEXT) }
152
- after = GC.stat[:total_allocated_objects]
153
- puts "format_wiki() allocations per call: ~#{(after - before) / 100}"
154
-
155
- puts
156
- puts "=" * 60
157
- puts "Benchmark complete"
158
- puts "=" * 60
159
- end
160
-
161
- run_benchmarks
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Fetches HTML named character references from WHATWG HTML specification
4
- # Usage: ruby scripts/fetch_html_entities.rb
5
- #
6
- # This script downloads the official entities.json from WHATWG and converts
7
- # it into a format suitable for wp2txt text processing.
8
-
9
- require "net/http"
10
- require "json"
11
- require "fileutils"
12
-
13
- WHATWG_ENTITIES_URL = "https://html.spec.whatwg.org/entities.json"
14
-
15
- def fetch_whatwg_entities
16
- puts "Fetching entities from WHATWG HTML specification..."
17
- uri = URI(WHATWG_ENTITIES_URL)
18
-
19
- response = Net::HTTP.get_response(uri)
20
- unless response.is_a?(Net::HTTPSuccess)
21
- warn "Failed to fetch entities: HTTP #{response.code}"
22
- return nil
23
- end
24
-
25
- JSON.parse(response.body)
26
- rescue StandardError => e
27
- warn "Error fetching entities: #{e.message}"
28
- nil
29
- end
30
-
31
- def convert_entities(raw_data)
32
- entities = {}
33
-
34
- raw_data.each do |name, info|
35
- # Only include entries with semicolon (standard form)
36
- # Skip legacy forms without semicolon like "&nbsp"
37
- next unless name.end_with?(";")
38
-
39
- # Extract entity name without & and ;
40
- # e.g., "&alpha;" -> "alpha"
41
- key = name
42
-
43
- # Get the character(s)
44
- characters = info["characters"]
45
- next if characters.nil? || characters.empty?
46
-
47
- entities[key] = characters
48
- end
49
-
50
- entities
51
- end
52
-
53
- def main
54
- raw_data = fetch_whatwg_entities
55
- if raw_data.nil?
56
- warn "Failed to fetch entities. Aborting."
57
- exit 1
58
- end
59
-
60
- puts "Processing #{raw_data.size} raw entries..."
61
-
62
- entities = convert_entities(raw_data)
63
- puts "Converted to #{entities.size} standard entities (with semicolon)"
64
-
65
- result = {
66
- "meta" => {
67
- "generated_at" => Time.now.utc.iso8601,
68
- "source" => WHATWG_ENTITIES_URL,
69
- "description" => "HTML named character references from WHATWG HTML specification",
70
- "total_entities" => entities.size
71
- },
72
- "entities" => entities.sort.to_h
73
- }
74
-
75
- # Write output
76
- output_path = File.join(__dir__, "..", "lib", "wp2txt", "data", "html_entities.json")
77
- FileUtils.mkdir_p(File.dirname(output_path))
78
-
79
- File.write(output_path, JSON.pretty_generate(result))
80
- puts "\nData written to: #{output_path}"
81
-
82
- # Summary - show some categories
83
- greek = entities.keys.select { |k| k.match?(/&(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega);/i) }
84
- math = entities.keys.select { |k| k.match?(/&(sum|prod|int|infin|nabla|part|forall|exist|empty|isin|notin|cap|cup|sub|sup|oplus|otimes);/i) }
85
- arrows = entities.keys.select { |k| k.match?(/arr;$/i) }
86
-
87
- puts "\n=== Summary ==="
88
- puts "Total entities: #{entities.size}"
89
- puts "Greek letters: #{greek.size}"
90
- puts "Math symbols: #{math.size}"
91
- puts "Arrows: #{arrows.size}"
92
- end
93
-
94
- main if __FILE__ == $PROGRAM_NAME
@@ -1,180 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Fetches Wikipedia language metadata from Wikimedia APIs
4
- # Usage: ruby scripts/fetch_language_metadata.rb
5
- #
6
- # This script queries the Wikimedia sitematrix API to get all Wikipedia
7
- # language editions and their statistics (article counts, etc.)
8
-
9
- require "net/http"
10
- require "json"
11
- require "fileutils"
12
-
13
- # Fetch all Wikipedia languages with statistics from sitematrix API
14
- def fetch_wikipedia_languages
15
- uri = URI("https://meta.wikimedia.org/w/api.php")
16
- params = {
17
- action: "sitematrix",
18
- smtype: "language",
19
- format: "json"
20
- }
21
- uri.query = URI.encode_www_form(params)
22
-
23
- response = Net::HTTP.get_response(uri)
24
- return {} unless response.is_a?(Net::HTTPSuccess)
25
-
26
- data = JSON.parse(response.body)
27
- languages = {}
28
-
29
- data["sitematrix"].each do |key, val|
30
- next unless key.match?(/^\d+$/) && val.is_a?(Hash) && val["site"]
31
-
32
- # Find Wikipedia site info
33
- wiki_site = val["site"].find { |site| site["code"] == "wiki" }
34
- next unless wiki_site
35
-
36
- lang_code = val["code"]
37
- languages[lang_code] = {
38
- "name" => val["name"],
39
- "localname" => val["localname"],
40
- "url" => wiki_site["url"],
41
- "dbname" => wiki_site["dbname"],
42
- "closed" => wiki_site["closed"] || false,
43
- "private" => wiki_site["private"] || false
44
- }
45
- end
46
-
47
- languages
48
- rescue StandardError => e
49
- warn "Error fetching sitematrix: #{e.message}"
50
- {}
51
- end
52
-
53
- # Fetch article statistics for a specific Wikipedia
54
- def fetch_wiki_statistics(lang_code)
55
- uri = URI("https://#{lang_code}.wikipedia.org/w/api.php")
56
- params = {
57
- action: "query",
58
- meta: "siteinfo",
59
- siprop: "statistics",
60
- format: "json"
61
- }
62
- uri.query = URI.encode_www_form(params)
63
-
64
- response = Net::HTTP.get_response(uri)
65
- return nil unless response.is_a?(Net::HTTPSuccess)
66
-
67
- data = JSON.parse(response.body)
68
- stats = data.dig("query", "statistics")
69
- return nil unless stats
70
-
71
- {
72
- "articles" => stats["articles"],
73
- "pages" => stats["pages"],
74
- "edits" => stats["edits"],
75
- "users" => stats["users"],
76
- "activeusers" => stats["activeusers"]
77
- }
78
- rescue StandardError
79
- nil
80
- end
81
-
82
- def main
83
- puts "Fetching Wikipedia language list..."
84
- languages = fetch_wikipedia_languages
85
-
86
- if languages.empty?
87
- warn "Failed to fetch language list. Aborting."
88
- exit 1
89
- end
90
-
91
- # Filter out closed/private wikis
92
- active_languages = languages.reject { |_, info| info["closed"] || info["private"] }
93
- puts "Found #{active_languages.size} active Wikipedia editions."
94
-
95
- puts "Fetching statistics for each Wikipedia (this may take a few minutes)..."
96
- successful = 0
97
- failed = []
98
-
99
- active_languages.each_with_index do |(lang_code, info), idx|
100
- print "\r Processing: #{lang_code.ljust(10)} (#{idx + 1}/#{active_languages.size})"
101
- $stdout.flush
102
-
103
- stats = fetch_wiki_statistics(lang_code)
104
- if stats
105
- info.merge!(stats)
106
- successful += 1
107
- else
108
- failed << lang_code
109
- end
110
-
111
- sleep 0.05 # Rate limiting
112
- end
113
-
114
- puts "\n Successfully fetched: #{successful}/#{active_languages.size}"
115
- puts " Failed: #{failed.size} (#{failed.first(10).join(', ')}#{failed.size > 10 ? '...' : ''})" if failed.any?
116
-
117
- # Categorize by size
118
- size_categories = {
119
- "large" => [], # 1M+ articles
120
- "medium" => [], # 100K-1M articles
121
- "small" => [], # 10K-100K articles
122
- "mini" => [] # <10K articles
123
- }
124
-
125
- active_languages.each do |lang_code, info|
126
- articles = info["articles"] || 0
127
- category = if articles >= 1_000_000
128
- "large"
129
- elsif articles >= 100_000
130
- "medium"
131
- elsif articles >= 10_000
132
- "small"
133
- else
134
- "mini"
135
- end
136
- size_categories[category] << lang_code
137
- info["size_category"] = category
138
- end
139
-
140
- # Build result
141
- result = {
142
- "meta" => {
143
- "generated_at" => Time.now.utc.iso8601,
144
- "source" => "Wikimedia sitematrix + siteinfo APIs",
145
- "total_languages" => active_languages.size,
146
- "statistics_fetched" => successful
147
- },
148
- "size_summary" => {
149
- "large" => size_categories["large"].size,
150
- "medium" => size_categories["medium"].size,
151
- "small" => size_categories["small"].size,
152
- "mini" => size_categories["mini"].size
153
- },
154
- "languages" => active_languages.sort_by { |_, info| -(info["articles"] || 0) }.to_h
155
- }
156
-
157
- # Write output
158
- output_path = File.join(__dir__, "..", "lib", "wp2txt", "data", "language_metadata.json")
159
- FileUtils.mkdir_p(File.dirname(output_path))
160
-
161
- File.write(output_path, JSON.pretty_generate(result))
162
- puts "\nData written to: #{output_path}"
163
-
164
- # Summary
165
- puts "\n=== Summary ==="
166
- puts "Total active Wikipedias: #{active_languages.size}"
167
- puts "Size categories:"
168
- puts " Large (1M+ articles): #{size_categories['large'].size} - #{size_categories['large'].first(5).join(', ')}..."
169
- puts " Medium (100K-1M): #{size_categories['medium'].size}"
170
- puts " Small (10K-100K): #{size_categories['small'].size}"
171
- puts " Mini (<10K): #{size_categories['mini'].size}"
172
-
173
- # Top 20 by article count
174
- puts "\nTop 20 Wikipedias by article count:"
175
- active_languages.sort_by { |_, info| -(info["articles"] || 0) }.first(20).each_with_index do |(code, info), idx|
176
- puts " #{(idx + 1).to_s.rjust(2)}. #{code.ljust(5)} #{info['name'].to_s.ljust(20)} #{(info['articles'] || 0).to_s.rjust(10)} articles"
177
- end
178
- end
179
-
180
- main if __FILE__ == $PROGRAM_NAME