rsssf 0.2.0 → 0.3.0

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/Manifest.txt +39 -2
  4. data/README.md +67 -62
  5. data/Rakefile +2 -2
  6. data/config/groups_en.txt +44 -0
  7. data/config/rounds_en.txt +283 -0
  8. data/config/rounds_es.txt +20 -0
  9. data/config/rounds_misc.txt +7 -0
  10. data/lib/_cocos_.rb +158 -0
  11. data/lib/rsssf/convert/convert.rb +71 -0
  12. data/lib/rsssf/convert/errata.rb +103 -0
  13. data/lib/rsssf/convert/html_entities.rb +150 -0
  14. data/lib/rsssf/convert/html_to_txt/beautify_anchors.rb +96 -0
  15. data/lib/rsssf/convert/html_to_txt/make_heading.rb +70 -0
  16. data/lib/rsssf/convert/html_to_txt/remove_emails.rb +43 -0
  17. data/lib/rsssf/convert/html_to_txt/replace_a_href.rb +85 -0
  18. data/lib/rsssf/convert/html_to_txt/replace_a_name.rb +87 -0
  19. data/lib/rsssf/convert/html_to_txt/replace_heading.rb +76 -0
  20. data/lib/rsssf/convert/html_to_txt/replace_hr.rb +25 -0
  21. data/lib/rsssf/convert/html_to_txt.rb +247 -0
  22. data/lib/rsssf/download.rb +4 -135
  23. data/lib/rsssf/fmtfix/dates.rb +541 -0
  24. data/lib/rsssf/fmtfix/dates_helpers.rb +63 -0
  25. data/lib/rsssf/fmtfix/errata.rb +44 -0
  26. data/lib/rsssf/fmtfix/fmtfix-base.rb +68 -0
  27. data/lib/rsssf/fmtfix/fmtfix.rb +101 -0
  28. data/lib/rsssf/fmtfix/goals.rb +173 -0
  29. data/lib/rsssf/fmtfix/headers.rb +326 -0
  30. data/lib/rsssf/fmtfix/outline.rb +228 -0
  31. data/lib/rsssf/fmtfix/patch_headings.rb +141 -0
  32. data/lib/rsssf/fmtfix/rounds.rb +74 -0
  33. data/lib/rsssf/fmtfix/score.rb +92 -0
  34. data/lib/rsssf/fmtfix/tables.rb +316 -0
  35. data/lib/rsssf/fmtfix/topscorers.rb +50 -0
  36. data/lib/rsssf/page-find_schedule.rb +127 -0
  37. data/lib/rsssf/page-meta.rb +68 -0
  38. data/lib/rsssf/page.rb +89 -227
  39. data/lib/rsssf/parse_schedules.rb +34 -0
  40. data/lib/rsssf/prepare/convert-links.rb +77 -0
  41. data/lib/rsssf/prepare/convert-meta.rb +111 -0
  42. data/lib/rsssf/prepare/convert-navlines.rb +154 -0
  43. data/lib/rsssf/prepare/convert-postproc.rb +141 -0
  44. data/lib/rsssf/prepare/convert.rb +100 -0
  45. data/lib/rsssf/prepare/download.rb +40 -0
  46. data/lib/rsssf/project.rb +154 -0
  47. data/lib/rsssf/reports/page.rb +40 -8
  48. data/lib/rsssf/reports/schedule.rb +18 -55
  49. data/lib/rsssf/utils.rb +28 -17
  50. data/lib/rsssf/version.rb +5 -2
  51. data/lib/rsssf.rb +53 -13
  52. metadata +50 -9
  53. data/lib/rsssf/convert.rb +0 -495
  54. data/lib/rsssf/repo.rb +0 -144
data/lib/rsssf/convert.rb DELETED
@@ -1,495 +0,0 @@
1
-
2
- module Rsssf
3
- class PageConverter
4
-
5
- ## convenience helper
6
- def self.convert( html, url: )
7
- @@converter ||= new ## use a "shared" built-in converter
8
- @@converter.convert( html, url: url )
9
- end
10
-
11
- ##
12
- ## add anchor: options or such
13
- ## lets you toggle adding anchors (§premier etc.) - why? why not?
14
-
15
- def convert( html, url: )
16
- ### todo/fix: first check if html is all ascii-7bit e.g.
17
- ## includes only chars from 64 to 127!!!
18
-
19
- ## normalize newlines
20
- ## remove \r (form feed) used by Windows; just use \n (new line)
21
- html = html.gsub( "\r", '' )
22
-
23
- ## check for html entities
24
- html = html.gsub( "ä", 'ä' )
25
- html = html.gsub( "ö", 'ö' )
26
- html = html.gsub( "ü", 'ü' )
27
- html = html.gsub( "Ä", 'Ä' )
28
- html = html.gsub( "Ö", 'Ö' )
29
- html = html.gsub( "Ü", 'Ü' )
30
- html = html.gsub( "ß", 'ß' )
31
-
32
- ## typos / autofix - keep - why? why not?
33
- html = html.gsub( "&oulm;", 'ö' ) ## support typo in entity (ö)
34
- html = html.gsub( "¨", 'ü' ) ## support typo in entity (ü) - why? why not?
35
- html = html.gsub( "&slig;", "ß" ) ## support typo in entity (ß)
36
- html = html.gsub( "&aaacute;", "á" ) ## typo for á
37
-
38
-
39
- html = html.gsub( "É", 'É' )
40
- html = html.gsub( "ø", 'ø' )
41
- html = html.gsub( "ã", 'ã' )
42
- html = html.gsub( "õ", 'õ' )
43
- html = html.gsub( "ô", 'ô' )
44
-
45
- entities = %w[
46
- À À
47
- Á Á
48
- Â Â
49
- Ã Ã
50
- Ä Ä
51
- Å Å
52
- à à
53
- á á
54
- â â
55
- ã ã
56
- ä ä
57
- å å
58
- Æ Æ
59
- æ æ
60
- ß ß
61
- Ç Ç
62
- ç ç
63
- È È
64
- É É
65
- Ê Ê
66
- Ë Ë
67
- è è
68
- é é
69
- ê ê
70
- ë ë
71
- Ì Ì
72
- Í Í
73
- Î Î
74
- Ï Ï
75
- ì ì
76
- í í
77
- î î
78
- ï ï
79
- Ñ Ñ
80
- ñ ñ
81
- Ò Ò
82
- Ó Ó
83
- Ô Ô
84
- Õ Õ
85
- Ö Ö
86
- ò ò
87
- ó ó
88
- ô ô
89
- õ õ
90
- ö ö
91
- Ø Ø
92
- ø ø
93
- Ù Ù
94
- Ú Ú
95
- Û Û
96
- Ü Ü
97
- ù ù
98
- ú ú
99
- û û
100
- ü ü
101
- Ý Ý
102
- ý ý
103
- ÿ ÿ
104
-
105
- < &lt;
106
- > &gt;
107
- & &amp;
108
- © &copy;
109
- ® &reg;
110
-
111
- Š &#352;
112
- š &#353;
113
- č &#269;
114
- ć &#263;
115
- Ž &#381;
116
- ’ &#8217;
117
- ]
118
-
119
-
120
-
121
- entities.each_slice(2) do |str, entity|
122
- html = html.gsub( entity, str )
123
- end
124
-
125
-
126
-
127
- ##############
128
- ## check for more entities
129
- ## limit &---; to length 10 - why? why not?
130
- html = html.gsub( /&[^; ]{1,10};/) do |match|
131
-
132
- match = if match == '&#307;' ## use like Van D&#307;k -> Van Dijk
133
- 'ij'
134
- else
135
- msg = "found unencoded html entity #{match}"
136
- puts "*** WARN - #{msg}"
137
- log( msg ) ## log too (see log.txt)
138
-
139
- match ## pass through as is (1:1)
140
- end
141
-
142
- match
143
- end
144
- ## todo/fix: add more entities
145
-
146
- ###################################
147
- ### smart quotes quick fixes
148
- ### convert all "smart" quote to (standard) single quotes
149
- ## D´Alessandro => D'Alessandro
150
-
151
- html = html.gsub( '´', "'" )
152
-
153
- html = html.gsub( '’', "'" )
154
- html = html.gsub( '‘', "'" )
155
- html = html.gsub( '“', '"' )
156
- html = html.gsub( '”', '"' )
157
-
158
- ### convert fancy dashes/hyphens to plain dash/hyphen
159
- html = html.gsub( '–', '-' )
160
-
161
-
162
-
163
- txt = html_to_txt( html )
164
-
165
- header = <<EOS
166
- <!--
167
- source: #{url}
168
- -->
169
-
170
- EOS
171
-
172
- header+txt ## return txt w/ header
173
- end ## method convert
174
-
175
-
176
- ## todo/fix - use generic heading regex for all h2/h3/h4 etc.
177
- ## exclude h1 - why? why not?
178
- ## note - include leading and trailing spaces !!!
179
- ##
180
- ## note - for content use non-greedy to allow
181
- ## match of tags inside content too
182
- HEADING2_RE = %r{ \s*
183
- <H2>
184
- (?<title>.+?)
185
- </H2>
186
- \s*
187
- }imx
188
-
189
- HEADING4_RE = %r{ \s*
190
- <H4>
191
- (?<title>.+?)
192
- </H4>
193
- \s*
194
- }imx
195
-
196
- def replace_h2( html )
197
- html.gsub( HEADING2_RE ) do |_|
198
- m = Regexp.last_match
199
- puts " replace heading 2 (h2) >#{m[:title]}<"
200
- "\n\n## #{m[:title]}\n\n" ## note: make sure to always add two newlines
201
- end
202
- end
203
-
204
- def replace_h4( html )
205
- html.gsub( HEADING4_RE ) do |_|
206
- m = Regexp.last_match
207
- puts " replace heading 4 (h4) >#{m[:title]}<"
208
- "\n\n#### #{m[:title]}\n\n" ## note: make sure to always add two newlines
209
- end
210
- end
211
-
212
-
213
- def squish( str )
214
- ## squish more than one white space to one space
215
- str.gsub( /[ \r\t\n]+/, ' ' )
216
- end
217
-
218
-
219
- def patch_about( html )
220
- # <A name=about>
221
- # <H2>About this document</H2></A>
222
- # or
223
- # <A NAME="about"><H2>About this document</H2></A>
224
- # => change to (possible?)
225
- # <H2><A name=about>About this document</A></H2>
226
-
227
- html.sub( %r{<A [ ] name=(about|"about")> \s*
228
- <H2>About [ ] this [ ] document</H2></A>
229
- }ixm,
230
- "<H2><A name=about>About this document</A></H2>"
231
- )
232
- end
233
-
234
- # <a name="sa">Série A</a>
235
- # <a name="sd">Série D</a>
236
-
237
- # <A name=about>
238
- # <H2>About this document</H2></A>
239
- # => change to (possible?)
240
- # <H2><A name=about>About this document</A></H2>
241
- #
242
- #
243
- # <h4><a name="cb">Copa do Brasil</a></h4>
244
-
245
- ## note - for content use non-greedy to allow
246
- ## match of tags inside content too
247
-
248
- A_NAME_RE = %r{<A [ ]+ NAME [ ]* =
249
- (?<name>[^>]+?)
250
- >
251
- (?<title>.+?)
252
- </A>
253
- }imx
254
-
255
- # <a href="#sa">Série A</a><br>
256
- #
257
- # <A href="http://www.rsssf.org/">Rec.Sport.Soccer
258
- # Statistics Foundation</A>
259
- # <A href="http://www.rsssfbrasil.com">RSSSF
260
- # Brazil</A>
261
- #
262
- # and Daniel Dalence (<A
263
- # href="mailto:danielballack@terra.com.br">danielballack@terra.com.br</A>)
264
-
265
-
266
- A_HREF_RE = %r{<A \s+ HREF [ ]* =
267
- (?<href>[^>]+?)
268
- >
269
- (?<title>.+?)
270
- <\/A>
271
- }imx
272
-
273
-
274
- def replace_a_href( html )
275
- ## remove anchors (a href)
276
- # note: heading 4 includes anchor (thus, let anchors go first)
277
- # note: <a \newline href is used for authors email - thus incl. support for newline as space
278
- html.gsub( A_HREF_RE ) do |match| ## note: use .+? non-greedy match
279
- m = Regexp.last_match
280
- href = m[:href].gsub( /["']/, '' ).strip ## remove ("" or '')
281
- title = m[:title].strip ## note: "save" caputure first; gets replaced by gsub (next regex call)
282
-
283
-
284
- ## e.g.
285
- ## ‹Larsen23@gmx.de, see page mailto:Larsen23@gmx.de›
286
- ## ‹danielballack@terra.com.br, see page mailto:danielballack@terra.com.br›
287
- ## ‹zja70@aol.com, see page mailto:zja70@aol.com›)
288
- if href.start_with?( 'mailto:')
289
- puts " blank mailto - anchor (a) href >#{href}, >#{title}<"
290
- '‹mailto›' ## delete/remove email
291
- else
292
- puts " replace anchor (a) href >#{href}, >#{title}<"
293
-
294
- ## convert href to xref
295
- xref = if href.start_with?('#') ## in-page ref
296
- ", see §#{href[1..-1]}"
297
- elsif href.start_with?( /https?:/ ) ## external page ref
298
- ## skip - keep empty - why? why not? (or add url domain?)
299
- ''
300
- else
301
- ## hack - check for some custom excludes
302
- if title.start_with?( 'Rec.Sport.Soccer' )
303
- ## skip - keep empty
304
- ''
305
- else
306
- ## strip (ending) .htm|html
307
- ", see page #{href.sub( /\.html?$/,'')}"
308
- end
309
- end
310
-
311
- "‹#{squish(title)}#{xref}›"
312
- end
313
- end
314
- end
315
-
316
- def replace_a_name( html )
317
- ##
318
- ## remove (named) anchors
319
- html.gsub( A_NAME_RE ) do |match| ## note: use .+? non-greedy match
320
- m = Regexp.last_match
321
- name = m[:name].gsub( /["']/, '' ).strip ## remove ("" or '')
322
- title = m[:title].strip ## note: "save" caputure first; gets replaced by gsub (next regex call)
323
- match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
324
- puts " replace anchor (a) name >#{name}<, >#{title}< - >#{match}<"
325
-
326
-
327
- ##
328
- ## todo - report WARN if title incl. tags
329
- ## assumes text only for now - why? why not?
330
- ## add a name inside heading !!!
331
- ## do NOT add heading inside a name !!!
332
-
333
- "#{title} ‹§#{name}›" ## note - use two spaces min (between title & name)
334
- end
335
- end
336
-
337
-
338
- EMAIL_RE = %r{ \s*
339
- \(
340
- [a-z][a-z0-9_]+
341
- @[a-z]+(\.[a-z]+)+
342
- \)
343
- }imx
344
-
345
-
346
- def remove_emails( html )
347
- ### remove converted ("blineded") mailto anchors
348
- ## note usually inside () e.g.
349
- ## (‹mailto›)
350
- ## plus slurp up all leading whitespace (incl. newline) - why? why not?
351
- html = html.gsub( /\s*
352
- \(‹mailto›\)
353
- /xm, '' )
354
-
355
- ###
356
- ## remove "regular emails too e.g.
357
- ##
358
- ## Thanks to Marcelo Leme de Arruda (___@___.__.br),
359
- ## Ricardo FF Pontes (___@____.com),
360
- ## Santiago Reis (____@____.com.br),
361
- ## Marcos Lacerda Queiroz (___@____.com.br)
362
- ## etc.
363
-
364
- ## check for "free-standing e.g. on its own line" emails only for now
365
- html = html.gsub( EMAIL_RE ) do |match|
366
- puts "removing email >#{match}<"
367
- ''
368
- end
369
- html
370
- end
371
-
372
-
373
-
374
- def html_to_txt( html )
375
-
376
- ###
377
- # todo: check if any tags (still) present??
378
-
379
-
380
- ## cut off everything before body
381
- html = html.sub( /.+?<BODY>\s*/im, '' )
382
-
383
- ## cut off everything after body (closing)
384
- html = html.sub( /<\/BODY>.*/im, '' )
385
-
386
- html = patch_about( html )
387
-
388
- ## remove cite
389
- html = html.gsub( /<CITE>([^<]+)<\/CITE>/im ) do |_|
390
- puts " remove cite >#{$1}<"
391
- "#{$1}"
392
- end
393
-
394
- html = html.gsub( /\s*<HR>\s*/im ) do |match|
395
- match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
396
- puts " replace horizontal rule (hr) - >#{match}<"
397
- "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" ## check what hr to use use - . - . - or =-=-=-= or somehting distinct?
398
- end
399
-
400
- ## replace break (br)
401
- ## note: do NOT use m/multiline for now - why? why not??
402
- html = html.gsub( /<BR>\s*/i ) do |match| ## note: include (swallow) "extra" newline
403
- match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
404
- puts " replace break (br) - >#{match}<"
405
- "\n"
406
- end
407
-
408
-
409
-
410
- html = replace_a_href( html )
411
- ## note a name="about" includes more a hrefs etc.
412
- # let it go first (before a href)
413
- html = replace_a_name( html )
414
-
415
-
416
-
417
- ## replace paragrah (p)
418
- html = html.gsub( /\s*<P>\s*/im ) do |match| ## note: include (swallow) "extra" newline
419
- match = match.gsub( "\n", '$$' ) ## make newlines visible for debugging
420
- puts " replace paragraph (p) - >#{match}<"
421
- "\n\n"
422
- end
423
- html = html.gsub( /<\/P>/i, '' ) ## replace paragraph (p) closing w/ nothing for now
424
-
425
- ## remove i
426
- html = html.gsub( /<I>([^<]+)<\/I>/im ) do |_|
427
- puts " remove italic (i) >#{$1}<"
428
- "#{$1}"
429
- end
430
-
431
-
432
- html = replace_h2( html )
433
- html = replace_h4( html )
434
-
435
-
436
-
437
-
438
- ## remove b - note: might include anchors (thus, call after anchors)
439
- html = html.gsub( /<B>([^<]+)<\/B>/im ) do |_|
440
- puts " remove bold (b) >#{$1}<"
441
- "**#{$1}**"
442
- end
443
-
444
- ## replace preformatted (pre)
445
- html = html.gsub( /<PRE>|<\/PRE>/i ) do |_|
446
- puts " replace preformatted (pre)"
447
- '' # replace w/ nothing for now (keep surrounding newlines)
448
- end
449
-
450
- =begin
451
- puts
452
- puts
453
- puts "html:"
454
- puts html[0..2000]
455
- puts "-- snip --"
456
- puts html[-1000..-1] ## print last hundred chars
457
- =end
458
-
459
-
460
- html = remove_emails( html )
461
-
462
-
463
- ## cleanup whitespaces
464
- ## todo/fix: convert newline in space first
465
- ## and than collapse spaces etc.!!!
466
- txt = String.new
467
- html.each_line do |line|
468
- line = line.gsub( "\t", ' ' ) # replace all tabs w/ two spaces for nwo
469
- line = line.rstrip # remove trailing whitespace (incl. newline/formfeed)
470
-
471
- txt << line
472
- txt << "\n"
473
- end
474
-
475
- txt
476
- end # method html_to_text
477
-
478
-
479
-
480
- ###
481
- # more helpers
482
- def log( msg )
483
- ## append msg to ./logs.txt
484
- ## use ./errors.txt - why? why not?
485
- File.open( './logs.txt', 'a:utf-8' ) do |f|
486
- f.write( msg )
487
- f.write( "\n" )
488
- end
489
- end
490
-
491
-
492
-
493
- end # module PageConverter
494
- end # module Rsssf
495
-
data/lib/rsssf/repo.rb DELETED
@@ -1,144 +0,0 @@
1
-
2
- module Rsssf
3
-
4
-
5
-
6
- class Repo
7
- include Utils ## e.g. year_from_file, etc.
8
-
9
-
10
- def initialize( path, title: 'Your Title Here',
11
- patch: nil )
12
- @repo_path = path
13
- @title = title
14
- @patch = patch
15
- end
16
-
17
-
18
- def root() @repo_path; end ## use/rename to path - why? why not?
19
- alias_method :root_dir, :root
20
-
21
-
22
- ## for now use single country repos - why? why not?
23
- ## add support for all-in-one repos
24
- def prepare_pages( code, seasons )
25
- seasons.each do |season|
26
- url = Rsssf.table_url( code, season: season )
27
-
28
- ## check if not in cache
29
- unless Webcache.cached?( url )
30
- ## download - if not cached
31
- Rsssf.download_table( code, season: season )
32
- end
33
-
34
- page = Page.read_cache( url )
35
-
36
- url_path = URI.parse( url ).path
37
- puts " url = >#{url}<"
38
- puts " url_path = >#{url_path}<"
39
-
40
- basename = File.basename( url_path, File.extname( url_path ))
41
-
42
- ###
43
- ## check for on_prepare (apply patches)
44
- if @patch && @patch.respond_to?(:on_prepare)
45
- year = year_from_name( basename )
46
- page.txt = @patch.on_prepare( page.txt, basename, year )
47
- end
48
-
49
-
50
- path = "#{@repo_path}/tables/#{basename}.txt"
51
- page.save( path )
52
- end
53
- end # method prepare_pages
54
-
55
-
56
- def each_page( code, seasons, &blk ) ## use each table or such - why? why not?
57
- seasons.each do |season|
58
- url = Rsssf.table_url( code, season: season )
59
- url_path = URI.parse( url ).path
60
- puts " url = >#{url}<"
61
- puts " url_path = >#{url_path}<"
62
- basename = File.basename( url_path, File.extname( url_path ))
63
-
64
- path = "#{@repo_path}/tables/#{basename}.txt"
65
- page = Page.read_txt( path )
66
-
67
- ## add/pass along patcher if patcher
68
- if @patch
69
- page.patch = @patch
70
- page.url = url
71
- end
72
-
73
- season = Season( season )
74
- blk.call( season, page )
75
- end
76
- end
77
-
78
-
79
- def make_pages_summary
80
- files = Dir.glob( "#{@repo_path}/tables/*.txt" )
81
- report = PageReport.build( files, title: @title ) ## pass in title etc.
82
-
83
- ### save report as README.md in tables/ folder in repo
84
- report.save( "#{@repo_path}/tables/README.md" )
85
- end # method make_pages_summary
86
-
87
-
88
- def make_schedules_summary
89
- ## find all match datafiles
90
- args = [@repo_path]
91
- files = SportDb::Parser::Opts.expand_args( args )
92
- pp files
93
-
94
- report = ScheduleReport.build( files, title: @title,
95
- patch: @patch ) ## pass in title etc.
96
- report.save( "#{@repo_path}/README.md" )
97
- end
98
-
99
-
100
-
101
-
102
- def patch_pages( patcher )
103
- ## lets you run/use custom (repo/country-specific patches e.g. for adding/patching headings etc.)
104
- patch_dir( "#{@repo_path}/tables" ) do |txt, name, year|
105
- puts "patching #{year} (#{name}) (#{@repo_path})..."
106
- patcher.patch( txt, name, year ) ## note: must be last (that is, must return (patcher) t(e)xt)
107
- end
108
- end ## method patch_pages
109
-
110
-
111
- def patch_dir( root, &blk )
112
- files = Dir.glob( "#{root}/**/*.txt" )
113
- ## pp files
114
-
115
- ## sort files by year (latest first)
116
- files = files.sort do |l,r|
117
- lyear = year_from_file( l )
118
- ryear = year_from_file( r )
119
-
120
- ryear <=> lyear
121
- end
122
-
123
- files.each do |file|
124
- txt = read_text( file ) ## note: assumes already converted to utf-8
125
-
126
- basename = File.basename( file, '.txt' ) ## e.g. duit92.txt => duit92
127
- year = year_from_name( basename )
128
-
129
- new_txt = blk.call( txt, basename, year )
130
-
131
- ## calculate hash to see if anything changed ?? why? why not??
132
- if txt != new_txt
133
- puts " patching #{file}, text changed"
134
- write_text( file, new_txt )
135
- end
136
- end # each file
137
- end ## patch_dir
138
-
139
-
140
-
141
-
142
- end ## class Repo
143
- end ## module Rsssf
144
-