sportdb-formats 1.1.2 → 1.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Manifest.txt +6 -13
  3. data/Rakefile +1 -1
  4. data/lib/sportdb/formats.rb +5 -0
  5. data/lib/sportdb/formats/country/country_index.rb +2 -2
  6. data/lib/sportdb/formats/event/event_index.rb +9 -11
  7. data/lib/sportdb/formats/league/league_index.rb +22 -18
  8. data/lib/sportdb/formats/league/league_outline_reader.rb +4 -1
  9. data/lib/sportdb/formats/league/league_reader.rb +7 -1
  10. data/lib/sportdb/formats/match/match_parser.rb +27 -15
  11. data/lib/sportdb/formats/match/match_parser_csv.rb +148 -21
  12. data/lib/sportdb/formats/match/match_status_parser.rb +86 -0
  13. data/lib/sportdb/formats/name_helper.rb +4 -1
  14. data/lib/sportdb/formats/package.rb +30 -8
  15. data/lib/sportdb/formats/score/score_formats.rb +19 -0
  16. data/lib/sportdb/formats/score/score_parser.rb +4 -2
  17. data/lib/sportdb/formats/structs/match.rb +2 -0
  18. data/lib/sportdb/formats/structs/team.rb +7 -0
  19. data/lib/sportdb/formats/team/club_index.rb +13 -11
  20. data/lib/sportdb/formats/team/club_index_history.rb +138 -0
  21. data/lib/sportdb/formats/team/club_reader_history.rb +203 -0
  22. data/lib/sportdb/formats/team/club_reader_props.rb +2 -3
  23. data/lib/sportdb/formats/version.rb +1 -1
  24. data/test/helper.rb +47 -81
  25. data/test/test_club_index_history.rb +107 -0
  26. data/test/test_club_reader_history.rb +212 -0
  27. data/test/test_datafile_package.rb +1 -1
  28. data/test/test_match_status_parser.rb +49 -0
  29. data/test/test_scores.rb +2 -0
  30. metadata +10 -17
  31. data/test/test_conf.rb +0 -65
  32. data/test/test_csv_match_parser.rb +0 -114
  33. data/test/test_csv_match_parser_utils.rb +0 -20
  34. data/test/test_match_auto.rb +0 -72
  35. data/test/test_match_auto_champs.rb +0 -45
  36. data/test/test_match_auto_euro.rb +0 -37
  37. data/test/test_match_auto_relegation.rb +0 -41
  38. data/test/test_match_auto_worldcup.rb +0 -61
  39. data/test/test_match_champs.rb +0 -27
  40. data/test/test_match_eng.rb +0 -26
  41. data/test/test_match_euro.rb +0 -27
  42. data/test/test_match_start_date.rb +0 -44
  43. data/test/test_match_worldcup.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89c3ae173bed0c7a68f30a897e4cf34da8d72d6e
4
- data.tar.gz: f3898e8b03177b62075cced2a90be2502555bf63
3
+ metadata.gz: 666fc1f16808dddbc988aa073c0d3b47c08a5934
4
+ data.tar.gz: 97437435e1f37341c9f2cc15c8b4e67adf0efe61
5
5
  SHA512:
6
- metadata.gz: 4c938af88f6dd86f1736532c9d8271a3e56f1769b955a31ce6d84a0b1fcf5a6eec7cfa42e0c019bd7072351d777a1988d6637c4297bee0dad9592bb8ec7450e8
7
- data.tar.gz: a80df68f64e13eeb9a85931e2d2dd4381f204f94a4ea9254c89b4b28346db4c1542321aed4fd123aef5ddedc7e4cd2c08731dd340326fae5dc94b57fa6797835
6
+ metadata.gz: 9498c5e378feab9fa1e80a8287f33ab608e606ecde6ae914997483850faffbbbc7695ef07d2cea1e04a529e724b01612e8231fe8625c19d0af34f3fabffd0a14
7
+ data.tar.gz: 28146d5c35a061f6c128a001c190681eb76de5471cde8124435e77d93ccca090bf0a84a7a6a09ccb67372f8edef0198e8f09390126629d14072901ff5fcb7743
@@ -20,6 +20,7 @@ lib/sportdb/formats/match/mapper_teams.rb
20
20
  lib/sportdb/formats/match/match_parser.rb
21
21
  lib/sportdb/formats/match/match_parser_auto_conf.rb
22
22
  lib/sportdb/formats/match/match_parser_csv.rb
23
+ lib/sportdb/formats/match/match_status_parser.rb
23
24
  lib/sportdb/formats/name_helper.rb
24
25
  lib/sportdb/formats/outline_reader.rb
25
26
  lib/sportdb/formats/package.rb
@@ -38,7 +39,9 @@ lib/sportdb/formats/structs/standings.rb
38
39
  lib/sportdb/formats/structs/team.rb
39
40
  lib/sportdb/formats/structs/team_usage.rb
40
41
  lib/sportdb/formats/team/club_index.rb
42
+ lib/sportdb/formats/team/club_index_history.rb
41
43
  lib/sportdb/formats/team/club_reader.rb
44
+ lib/sportdb/formats/team/club_reader_history.rb
42
45
  lib/sportdb/formats/team/club_reader_props.rb
43
46
  lib/sportdb/formats/team/national_team_index.rb
44
47
  lib/sportdb/formats/team/team_index.rb
@@ -46,14 +49,13 @@ lib/sportdb/formats/team/wiki_reader.rb
46
49
  lib/sportdb/formats/version.rb
47
50
  test/helper.rb
48
51
  test/test_club_index.rb
52
+ test/test_club_index_history.rb
49
53
  test/test_club_reader.rb
54
+ test/test_club_reader_history.rb
50
55
  test/test_club_reader_props.rb
51
56
  test/test_clubs.rb
52
- test/test_conf.rb
53
57
  test/test_country_index.rb
54
58
  test/test_country_reader.rb
55
- test/test_csv_match_parser.rb
56
- test/test_csv_match_parser_utils.rb
57
59
  test/test_csv_reader.rb
58
60
  test/test_datafile.rb
59
61
  test/test_datafile_package.rb
@@ -62,16 +64,7 @@ test/test_league_index.rb
62
64
  test/test_league_outline_reader.rb
63
65
  test/test_league_reader.rb
64
66
  test/test_match.rb
65
- test/test_match_auto.rb
66
- test/test_match_auto_champs.rb
67
- test/test_match_auto_euro.rb
68
- test/test_match_auto_relegation.rb
69
- test/test_match_auto_worldcup.rb
70
- test/test_match_champs.rb
71
- test/test_match_eng.rb
72
- test/test_match_euro.rb
73
- test/test_match_start_date.rb
74
- test/test_match_worldcup.rb
67
+ test/test_match_status_parser.rb
75
68
  test/test_name_helper.rb
76
69
  test/test_outline_reader.rb
77
70
  test/test_package.rb
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ Hoe.spec 'sportdb-formats' do
23
23
  ['alphabets', '>= 1.0.0'],
24
24
  ['date-formats', '>= 1.0.1'],
25
25
  ['csvreader', '>= 1.2.4'],
26
- ['sportdb-langs', '>= 0.1.0'],
26
+ ['sportdb-langs', '>= 0.1.1'],
27
27
 
28
28
  ['rubyzip', '>= 1.2.4' ],
29
29
  ]
@@ -75,10 +75,12 @@ require 'sportdb/formats/goals'
75
75
 
76
76
  require 'sportdb/formats/match/mapper'
77
77
  require 'sportdb/formats/match/mapper_teams'
78
+ require 'sportdb/formats/match/match_status_parser'
78
79
  require 'sportdb/formats/match/match_parser'
79
80
  require 'sportdb/formats/match/match_parser_auto_conf'
80
81
  require 'sportdb/formats/match/conf_parser'
81
82
 
83
+
82
84
  require 'sportdb/formats/match/match_parser_csv'
83
85
 
84
86
  require 'sportdb/formats/country/country_reader'
@@ -119,6 +121,9 @@ require 'sportdb/formats/team/wiki_reader'
119
121
  require 'sportdb/formats/team/national_team_index'
120
122
  require 'sportdb/formats/team/team_index'
121
123
 
124
+ require 'sportdb/formats/team/club_reader_history'
125
+ require 'sportdb/formats/team/club_index_history'
126
+
122
127
 
123
128
  ###
124
129
  # add convenience helpers / shortcuts
@@ -109,12 +109,12 @@ class CountryIndex
109
109
  @countries_by_name[ name ]
110
110
  end
111
111
 
112
- def []( key )
112
+ def find( key )
113
113
  country = find_by_code( key )
114
114
  country = find_by_name( key ) if country.nil? ## try lookup / find by (normalized) name
115
115
  country
116
116
  end
117
- alias_method :find, :[]
117
+ alias_method :[], :find
118
118
 
119
119
 
120
120
  ###
@@ -2,27 +2,25 @@ module SportDb
2
2
  module Import
3
3
 
4
4
 
5
+
5
6
  class EventIndex
6
7
 
7
8
  def self.build( path )
8
- datafiles = Package.find_seasons( path )
9
-
10
- puts
11
- puts "#{datafiles.size} seasons datafile(s):"
12
- pp datafiles
13
-
14
- index = new
15
- datafiles.each do |datafile|
16
- recs = EventInfoReader.read( datafile )
17
- # pp recs
9
+ pack = Package.new( path ) ## lets us use direcotry or zip archive
18
10
 
19
- index.add( recs )
11
+ recs = []
12
+ pack.each_seasons do |entry|
13
+ recs += EventInfoReader.parse( entry.read )
20
14
  end
15
+ recs
21
16
 
17
+ index = new
18
+ index.add( recs )
22
19
  index
23
20
  end
24
21
 
25
22
 
23
+
26
24
  attr_reader :events
27
25
  def initialize
28
26
  @events = []
@@ -95,36 +95,40 @@ class LeagueIndex
95
95
  end # method add
96
96
 
97
97
 
98
+ ## helper to always convert (possible) country key to existing country record
99
+ ## todo: make private - why? why not?
100
+ def country( country )
101
+ if country.is_a?( String ) || country.is_a?( Symbol )
102
+ ## note: use own "global" countries index setting for ClubIndex - why? why not?
103
+ rec = catalog.countries.find( country.to_s )
104
+ if rec.nil?
105
+ puts "** !!! ERROR !!! - unknown country >#{country}< - no match found, sorry - add to world/countries.txt in config"
106
+ exit 1
107
+ end
108
+ rec
109
+ else
110
+ country ## (re)use country struct - no need to run lookup again
111
+ end
112
+ end
113
+
114
+
98
115
  def match( name )
99
- ## todo/check: return empty array if no match!!! and NOT nil (add || []) - why? why not?
116
+ ## note: returns empty array if no match and NOT nil
100
117
  name = normalize( name )
101
- @leagues_by_name[ name ]
118
+ @leagues_by_name[ name ] || []
102
119
  end
103
120
 
104
-
105
121
  def match_by( name:, country: )
106
122
  ## note: match must for now always include name
107
123
  m = match( name )
108
- if m ## filter by country
124
+ if country ## filter by country
109
125
  ## note: country assumes / allows the country key or fifa code for now
110
-
111
126
  ## note: allow passing in of country struct too
112
- country_rec = if country.is_a?( Country )
113
- country ## (re)use country struct - no need to run lookup again
114
- else
115
- ## note: use own "global" countries index setting for ClubIndex - why? why not?
116
- rec = catalog.countries.find( country )
117
- if rec.nil?
118
- puts "** !!! ERROR !!! - unknown country >#{country}< - no match found, sorry - add to world/countries.txt in config"
119
- exit 1
120
- end
121
- rec
122
- end
127
+ country_rec = country( country )
123
128
 
124
129
  ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
125
130
  m = m.select { |league| league.country &&
126
131
  league.country.key == country_rec.key }
127
- m = nil if m.empty? ## note: reset to nil if no more matches
128
132
  end
129
133
  m
130
134
  end
@@ -144,7 +148,7 @@ class LeagueIndex
144
148
  m = match( name )
145
149
  # pp m
146
150
 
147
- if m.nil?
151
+ if m.empty?
148
152
  ## fall through/do nothing
149
153
  elsif m.size > 1
150
154
  puts "** !!! ERROR - ambigious league name; too many leagues (#{m.size}) found:"
@@ -127,7 +127,7 @@ class LeagueOutlineReader ## todo/check - rename to LeaguePageReader / LeagueP
127
127
  'Regular Season',
128
128
  'Regular Stage',
129
129
  'Championship Round',
130
- 'Championship Playoff',
130
+ 'Championship Playoff', # or Championship play-off
131
131
  'Relegation Round',
132
132
  'Relegation Playoff',
133
133
  'Play-offs',
@@ -140,6 +140,9 @@ class LeagueOutlineReader ## todo/check - rename to LeaguePageReader / LeagueP
140
140
  'EL Play-off',
141
141
  'Europa League Play-off',
142
142
  'Europa-League-Play-offs',
143
+ 'Playoffs - Championship',
144
+ 'Playoffs - Relegation',
145
+ 'Finals',
143
146
  ].map {|name| name.downcase.gsub( /[^a-z]/, '' ) }
144
147
 
145
148
 
@@ -118,12 +118,18 @@ def parse
118
118
  alt_names_auto << "#{country.code}" if league_key == '1' ## add shortcut for top level 1 (just country key)
119
119
  end
120
120
  alt_names_auto << "#{country.name} #{league_key}" if league_key =~ /^[0-9]+$/ ## if all numeric e.g. add Austria 1 etc.
121
+
122
+ ## auto-add with country prepended
123
+ ## e.g. England Premier League, Austria Bundesliga etc.
124
+ ## todo/check: also add variants with country alt name if present!!!
125
+ ## todo/check: exclude cups or such from country + league name auto-add - why? why not?
126
+ alt_names_auto << "#{country.name} #{league_name}"
121
127
  else ## assume int'l (no country) e.g. champions league, etc.
122
128
  ## only auto-add key (e.g. CL, EL, etc.)
123
129
  alt_names_auto << league_key.upcase.gsub('.', ' ') ## note: no country code (prefix/leading) used
124
130
  end
125
131
 
126
- pp alt_names_auto
132
+ ## pp alt_names_auto
127
133
 
128
134
  ## prepend country key/code if country present
129
135
  ## todo/fix: only auto-prepend country if key/code start with a number (level) or incl. cup
@@ -302,6 +302,11 @@ class MatchParser ## simple match parser for team match schedules
302
302
  ScoreFormats.find!( line )
303
303
  end
304
304
 
305
+ def find_status!( line )
306
+ StatusParser.find!( line )
307
+ end
308
+
309
+
305
310
  def try_parse_game( line )
306
311
  # note: clone line; for possible test do NOT modify in place for now
307
312
  # note: returns true if parsed, false if no match
@@ -329,6 +334,10 @@ class MatchParser ## simple match parser for team match schedules
329
334
  return false
330
335
  end
331
336
 
337
+ ## find (optional) match status e.g. [abandoned] or [replay] or [awarded]
338
+ ## or [cancelled] or [postponed] etc.
339
+ status = find_status!( line ) ## todo/check: allow match status also in geo part (e.g. after @) - why? why not?
340
+
332
341
  ## pos = find_game_pos!( line )
333
342
 
334
343
  date = find_date!( line, start: @start )
@@ -353,20 +362,23 @@ class MatchParser ## simple match parser for team match schedules
353
362
  if @last_round
354
363
  round = @last_round
355
364
  else
356
- ## find (first) matching round by date
357
- @rounds.values.each do |round_rec|
358
- ## note: convert date to date only (no time) with to_date!!!
359
- if (round_rec.start_date && round_rec.end_date) &&
360
- (date.to_date >= round_rec.start_date &&
361
- date.to_date <= round_rec.end_date)
362
- round = round_rec
363
- break
365
+ ## find (first) matching round by date if rounds / matchdays defined
366
+ ## if not rounds / matchdays defined - YES, allow matches WITHOUT rounds!!!
367
+ if @rounds.size > 0
368
+ @rounds.values.each do |round_rec|
369
+ ## note: convert date to date only (no time) with to_date!!!
370
+ if (round_rec.start_date && round_rec.end_date) &&
371
+ (date.to_date >= round_rec.start_date &&
372
+ date.to_date <= round_rec.end_date)
373
+ round = round_rec
374
+ break
375
+ end
376
+ end
377
+ if round.nil?
378
+ puts "!! ERROR - no matching round found for match date:"
379
+ pp date
380
+ exit 1
364
381
  end
365
- end
366
- if round.nil?
367
- puts "!! ERROR - no matching round found for match date:"
368
- pp date
369
- exit 1
370
382
  end
371
383
  end
372
384
 
@@ -380,8 +392,8 @@ class MatchParser ## simple match parser for team match schedules
380
392
  team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.name)
381
393
  score: score,
382
394
  round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event)
383
- group: @last_group ? @last_group.name : nil ) ## note: for now always use string (assume unique canonical name for event)
384
-
395
+ group: @last_group ? @last_group.name : nil, ## note: for now always use string (assume unique canonical name for event)
396
+ status: status )
385
397
  ### todo: cache team lookups in hash?
386
398
 
387
399
  =begin
@@ -101,9 +101,23 @@ module SportDb
101
101
  header_stage = find_header( headers, ['Stage'] )
102
102
  headers_mapping[:stage] = header_stage if header_stage
103
103
 
104
+ header_group = find_header( headers, ['Group'] )
105
+ headers_mapping[:group] = header_group if header_group
106
+
107
+
108
+ header_et = find_header( headers, ['ET', 'AET'] ) ## (after) extra time
109
+ headers_mapping[:score_et] = header_et if header_et
110
+
111
+ header_p = find_header( headers, ['P', 'PEN'] ) ## penalties
112
+ headers_mapping[:score_p] = header_p if header_p
113
+
114
+ header_notes = find_header( headers, ['Notes', 'Comments'] )
115
+ headers_mapping[:notes] = header_notes if header_notes
116
+
117
+
104
118
  header_league = find_header( headers, ['League'] )
105
119
  headers_mapping[:league] = header_league if header_league
106
- else
120
+ else
107
121
  ## else try footballdata.uk and others
108
122
  headers_mapping[:team1] = find_header( headers, ['HomeTeam', 'HT', 'Home'] )
109
123
  headers_mapping[:team2] = find_header( headers, ['AwayTeam', 'AT', 'Away'] )
@@ -229,7 +243,15 @@ module SportDb
229
243
  if headers_mapping[ :round ]
230
244
  col = row[ headers_mapping[ :round ]]
231
245
  ## todo: issue warning if not ? or - (and just empty string) why? why not
232
- round = col.to_i if col =~ /^\d{1,2}$/ # check format - e.g. ignore ? or - or such non-numbers for now
246
+ ## (old attic) was: round = col.to_i if col =~ /^\d{1,2}$/ # check format - e.g. ignore ? or - or such non-numbers for now
247
+
248
+ ## note: make round always a string for now!!!! e.g. "1", "2" too!!
249
+ round = if col.nil? || col.empty? || col == '-' || col == 'n/a'
250
+ ## note: allow missing round for match / defaults to nil
251
+ nil
252
+ else
253
+ col
254
+ end
233
255
  end
234
256
 
235
257
 
@@ -258,25 +280,65 @@ module SportDb
258
280
  score2i = ht[1].to_i if ht[1] =~ /^\d{1,2}$/
259
281
  end
260
282
 
283
+
261
284
  ## check for all-in-one full time scores?
262
285
  if headers_mapping[ :score ]
263
- ft = row[ headers_mapping[ :score ] ]
264
- if ft =~ /^\d{1,2}[\-:]\d{1,2}$/ ## sanity check scores format
265
- scores = ft.split( /[\-:]/ )
266
- score1 = scores[0].to_i
267
- score2 = scores[1].to_i
286
+ col = row[ headers_mapping[ :score ]]
287
+ score = parse_score( col )
288
+ if score
289
+ score1 = score[0]
290
+ score2 = score[1]
291
+ else
292
+ puts "!! ERROR - invalid score (ft) format >#{col}<:"
293
+ pp row
294
+ exit 1
268
295
  end
269
- ## todo/fix: issue warning if non-empty!!! and not matching format!!!!
270
296
  end
271
297
 
272
298
  if headers_mapping[ :scorei ]
273
- ht = row[ headers_mapping[ :scorei ] ]
274
- if ht =~ /^\d{1,2}[\-:]\d{1,2}$/ ## sanity check scores format
275
- scores = ht.split( /[\-:]/) ## allow 1-1 and 1:1
276
- score1i = scores[0].to_i
277
- score2i = scores[1].to_i
299
+ col = row[ headers_mapping[ :scorei ]]
300
+ score = parse_score( col )
301
+ if score
302
+ score1i = score[0]
303
+ score2i = score[1]
304
+ else
305
+ puts "!! ERROR - invalid score (ht) format >#{col}<:"
306
+ pp row
307
+ exit 1
308
+ end
309
+ end
310
+
311
+ ####
312
+ ## try optional score - extra time (et) and penalities (p/pen)
313
+ score1et = nil
314
+ score2et = nil
315
+ score1p = nil
316
+ score2p = nil
317
+
318
+ if headers_mapping[ :score_et ]
319
+ col = row[ headers_mapping[ :score_et ]]
320
+ score = parse_score( col )
321
+ if score
322
+ score1et = score[0]
323
+ score2et = score[1]
324
+ else
325
+ puts "!! ERROR - invalid score (et) format >#{col}<:"
326
+ pp row
327
+ exit 1
328
+ end
329
+ end
330
+
331
+ if headers_mapping[ :score_p ]
332
+ col = row[ headers_mapping[ :score_p ]]
333
+ score = parse_score( col )
334
+ if score
335
+ score1p = score[0]
336
+ score2p = score[1]
337
+ else
338
+ puts "!! ERROR - invalid score (p) format >#{col}<:"
339
+ pp row
340
+ exit 1
278
341
  end
279
- ## todo/fix: issue warning if non-empty!!! and not matching format!!!!
280
342
  end
281
343
 
282
344
 
@@ -296,17 +358,51 @@ module SportDb
296
358
  end
297
359
  end
298
360
 
361
+ group = nil
362
+ if headers_mapping[ :group ]
363
+ col = row[ headers_mapping[ :group ]]
364
+ ## todo/fix: check can col be nil e.g. col.nil? possible?
365
+ group = if col.nil? || col.empty? || col == '-' || col == 'n/a'
366
+ ## note: allow missing stage for match / defaults to "regular"
367
+ nil
368
+ else
369
+ col
370
+ end
371
+ end
372
+
373
+ status = nil ## e.g. AWARDED, CANCELLED, POSTPONED, etc.
374
+ if headers_mapping[ :notes ]
375
+ col = row[ headers_mapping[ :notes ]]
376
+ ## check for optional (match) status in notes / comments
377
+ status = if col.nil? || col.empty? || col == '-' || col == 'n/a'
378
+ nil
379
+ else
380
+ StatusParser.parse( col ) # note: returns nil if no (match) status found
381
+ end
382
+ end
383
+
384
+
299
385
  league = nil
300
386
  league = row[ headers_mapping[ :league ]] if headers_mapping[ :league ]
301
387
 
302
388
 
303
- match = Import::Match.new( date: date,
304
- team1: team1, team2: team2,
305
- score1: score1, score2: score2,
306
- score1i: score1i, score2i: score2i,
307
- round: round,
308
- stage: stage,
309
- league: league )
389
+ ## puts 'match attributes:'
390
+ attributes = {
391
+ date: date,
392
+ team1: team1, team2: team2,
393
+ score1: score1, score2: score2,
394
+ score1i: score1i, score2i: score2i,
395
+ score1et: score1et, score2et: score2et,
396
+ score1p: score1p, score2p: score2p,
397
+ round: round,
398
+ stage: stage,
399
+ group: group,
400
+ status: status,
401
+ league: league
402
+ }
403
+ ## pp attributes
404
+
405
+ match = Import::Match.new( **attributes )
310
406
  matches << match
311
407
  end
312
408
 
@@ -326,6 +422,37 @@ module SportDb
326
422
  nil ## no matching header found!!!
327
423
  end
328
424
 
425
+ ########
426
+ # more helpers
427
+ #
428
+
429
+ def parse_score( str )
430
+ if str.nil? ## todo/check: remove nil case - possible? - why? why not?
431
+ [nil,nil]
432
+ else
433
+ ## remove (optional single) note/footnote/endnote markers
434
+ ## e.g. (*) or (a), (b),
435
+ ## or [*], [A], [1], etc.
436
+ ## - allow (1) or maybe (*1) in the future - why? why not?
437
+ str = str.sub( /\( [a-z*] \)
438
+ |
439
+ \[ [1-9a-z*] \]
440
+ /ix, '' ).strip
441
+
442
+ if str.empty? || str == '?' || str == '-' || str == 'n/a'
443
+ [nil,nil]
444
+ ### todo/check: use regex with named capture groups here - why? why not?
445
+ elsif str =~ /^\d{1,2}[:-]\d{1,2}$/ ## sanity check scores format
446
+ score = str.split( /[:-]/ )
447
+ [score[0].to_i, score[1].to_i]
448
+ else
449
+ nil ## note: returns nil if invalid / unparseable format!!!
450
+ end
451
+ end
452
+ end # method parse_score
453
+
454
+
455
+
329
456
  end # class CsvMatchParser
330
457
  end # module SportDb
331
458