sportdb 1.6.12 → 1.6.13

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt CHANGED
@@ -42,6 +42,7 @@ lib/sportdb/models/track.rb
42
42
  lib/sportdb/reader.rb
43
43
  lib/sportdb/schema.rb
44
44
  lib/sportdb/stats.rb
45
+ lib/sportdb/title.rb
45
46
  lib/sportdb/utils.rb
46
47
  lib/sportdb/version.rb
47
48
  tasks/test.rb
data/lib/sportdb.rb CHANGED
@@ -28,6 +28,7 @@ require 'worlddb'
28
28
 
29
29
  require 'sportdb/version'
30
30
 
31
+ require 'sportdb/title' ## fix - move to textutils gem
31
32
  require 'sportdb/models/forward'
32
33
  require 'sportdb/models/badge'
33
34
  require 'sportdb/models/city'
@@ -48,7 +48,7 @@ class Event < ActiveRecord::Base
48
48
 
49
49
 
50
50
  def known_teams_table
51
- @known_teams_table ||= build_match_table_for( teams )
51
+ @known_teams_table ||= TextUtils.build_title_table_for( teams )
52
52
  end # method known_teams_table
53
53
 
54
54
  end # class Event
@@ -34,6 +34,8 @@ class League < ActiveRecord::Base
34
34
  end
35
35
  end
36
36
 
37
+ logger.debug " find league key: #{new_attributes[ :key ]}"
38
+
37
39
  rec = League.find_by_key( new_attributes[ :key ] )
38
40
  if rec.present?
39
41
  logger.debug "update League #{rec.id}-#{rec.key}:"
@@ -28,6 +28,14 @@ class Person < ActiveRecord::Base
28
28
  if value =~ /^[a-z]{2}$/ ## assume two-letter country key e.g. at,de,mx,etc.
29
29
  value_country = Country.find_by_key!( value )
30
30
  new_attributes[ :country_id ] = value_country.id
31
+ elsif value =~ /^[A-Z]{3}$/ ## assume three-letter code e.g. AUS, MAL, etc.
32
+ new_attributes[ :code ] = value
33
+ elsif value =~ /^([0-9]{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s([0-9]{4})$/ ## assume birthday
34
+ value_date_str = '%02d/%s/%d' % [$1, $2, $3] ## move to matcher!!
35
+ value_date = Date.strptime( value_date_str, '%d/%b/%Y' ) ## %b - abbreviated month name (e.g. Jan,Feb, etc.)
36
+ logger.debug " birthday #{value_date_str} - #{value_date}"
37
+ new_attributes[ :born_at ] = value_date
38
+ ## todo: convert to date
31
39
  else
32
40
  ## todo: assume title2 ??
33
41
  ## assume title2 if title2 is empty (not already in use)
@@ -13,7 +13,7 @@ class Track < ActiveRecord::Base
13
13
  ### fix: move known_tracks_table to event!!! (e.g. scoped by event)
14
14
 
15
15
  def self.known_tracks_table
16
- @@known_tracks_table ||= build_match_table_for( Track.all )
16
+ @@known_tracks_table ||= TextUtils.build_title_table_for( Track.all )
17
17
  end # method known_tracks_table
18
18
 
19
19
 
@@ -27,6 +27,8 @@ class Track < ActiveRecord::Base
27
27
  if value =~ /^[a-z]{2}$/ ## assume two-letter country key e.g. at,de,mx,etc.
28
28
  value_country = Country.find_by_key!( value )
29
29
  new_attributes[ :country_id ] = value_country.id
30
+ elsif value =~ /^[A-Z]{3}$/ ## assume three-letter code e.g. AUS, MAL, etc.
31
+ new_attributes[ :code ] = value
30
32
  else
31
33
  ## todo: assume title2 ??
32
34
  ## assume title2 if title2 is empty (not already in use)
@@ -94,7 +94,10 @@ class Reader
94
94
  elsif name =~ /\/squads/ || name =~ /\/rosters/ # e.g. 2013/squads.txt in formula1.db
95
95
  load_rosters( name )
96
96
  elsif name =~ /\/([0-9]{2})-/
97
- load_records( name ) # e.g. 2013/04-gp-monaco.txt in formula1.db
97
+ race_pos = $1.to_i
98
+ # NB: assume @event is set from previous load
99
+ race = Race.find_by_event_id_and_pos( @event.id, race_pos )
100
+ load_records( name, race_id: race.id ) # e.g. 2013/04-gp-monaco.txt in formula1.db
98
101
  elsif name =~ /^seasons/
99
102
  load_seasons( name )
100
103
  elsif name =~ /^leagues/
@@ -187,7 +190,11 @@ class Reader
187
190
 
188
191
  def load_seasons( name )
189
192
 
190
- reader = HashReaderV2.new( name, include_path )
193
+ path = "#{include_path}/#{name}.yml"
194
+
195
+ logger.info "parsing data '#{name}' (#{path})..."
196
+
197
+ reader = HashReader.new( path )
191
198
 
192
199
  ####
193
200
  ## fix!!!!!
@@ -207,6 +214,7 @@ class Reader
207
214
  value.each do |item|
208
215
  season_attribs = {}
209
216
 
217
+ logger.debug " find season key: #{item.to_s.strip}"
210
218
  season = Season.find_by_key( item.to_s.strip )
211
219
 
212
220
  ## check if it exists
@@ -231,9 +239,47 @@ class Reader
231
239
 
232
240
  end # each key,value
233
241
 
242
+ Prop.create_from_fixture!( name, path )
243
+
234
244
  end # load_seasons
235
245
 
236
246
 
247
+ def fetch_event( name )
248
+ # get/fetch/find event from yml file
249
+
250
+ path = "#{include_path}/#{name}.yml"
251
+
252
+ logger.info "parsing data '#{name}' (#{path})..."
253
+
254
+
255
+ ## todo/fix: use h = HashFile.load( path ) or similar instead of HashReader!!
256
+
257
+ reader = HashReader.new( path )
258
+
259
+ event_attribs = {}
260
+
261
+ reader.each_typed do |key, value|
262
+
263
+ ## puts "processing event attrib >>#{key}<< >>#{value}<<..."
264
+
265
+ if key == 'league'
266
+ league = League.find_by_key!( value.to_s.strip )
267
+ event_attribs[ 'league_id' ] = league.id
268
+ elsif key == 'season'
269
+ season = Season.find_by_key!( value.to_s.strip )
270
+ event_attribs[ 'season_id' ] = season.id
271
+ else
272
+ # skip; do nothing
273
+ end
274
+ end # each key,value
275
+
276
+ league_id = event_attribs['league_id']
277
+ season_id = event_attribs['season_id']
278
+
279
+ event = Event.find_by_league_id_and_season_id!( league_id, season_id )
280
+ event
281
+ end
282
+
237
283
 
238
284
  def load_event( name )
239
285
 
@@ -243,7 +289,11 @@ class Reader
243
289
  ## use Event.create_or_update_from_hash_reader?? or similar
244
290
  # move parsing code to model
245
291
 
246
- reader = HashReaderV2.new( name, include_path )
292
+ path = "#{include_path}/#{name}.yml"
293
+
294
+ logger.info "parsing data '#{name}' (#{path})..."
295
+
296
+ reader = HashReader.new( path )
247
297
 
248
298
  event_attribs = {}
249
299
 
@@ -306,7 +356,12 @@ class Reader
306
356
 
307
357
  end # each key,value
308
358
 
309
- event = Event.find_by_league_id_and_season_id( event_attribs['league_id'], event_attribs['season_id'])
359
+ league_id = event_attribs['league_id']
360
+ season_id = event_attribs['season_id']
361
+
362
+ logger.debug "find event - league_id: #{league_id}, season_id: #{season_id}"
363
+
364
+ event = Event.find_by_league_id_and_season_id( league_id, season_id )
310
365
 
311
366
  ## check if it exists
312
367
  if event.present?
@@ -320,6 +375,8 @@ class Reader
320
375
 
321
376
  event.update_attributes!( event_attribs )
322
377
 
378
+ Prop.create_from_fixture!( name, path )
379
+
323
380
  end # load_event
324
381
 
325
382
 
@@ -351,7 +408,7 @@ class Reader
351
408
 
352
409
 
353
410
 
354
- def load_records( name )
411
+ def load_records( name, more_attribs={} )
355
412
  path = "#{include_path}/#{name}.txt"
356
413
 
357
414
  logger.info "parsing data '#{name}' (#{path})..."
@@ -364,30 +421,66 @@ class Reader
364
421
  # @known_tracks = Track.known_tracks_table
365
422
 
366
423
  ## fix: add @known_teams - for now; use teams (not scoped by event)
424
+ @known_teams = TextUtils.build_title_table_for( Team.all )
425
+ ## and for now use all persons
426
+ @known_persons = TextUtils.build_title_table_for( Person.all )
367
427
 
368
- load_records_worker( reader )
428
+ load_records_worker( reader, more_attribs )
369
429
 
370
430
  Prop.create_from_fixture!( name, path )
371
431
  end
372
432
 
373
- def load_records_worker( reader )
433
+ def load_records_worker( reader, more_attribs )
374
434
 
375
435
  reader.each_line do |line|
376
436
  logger.debug " line: >#{line}<"
377
437
 
378
- ### fix: use new find_leading_pos!
379
- # pos = find_game_pos!( line ) # alias -> rename to find_pos! or better use find_leading_pos!( line )
438
+ cut_off_end_of_line_comment!( line )
380
439
 
381
- # match_person!( line )
440
+ state = find_record_leading_state!( line )
382
441
 
383
- # match_teams!( line )
442
+ map_team!( line )
443
+ team_key = find_team!( line )
444
+ team = Team.find_by_key!( team_key )
445
+
446
+ map_person!( line )
447
+ person_key = find_person!( line )
448
+ person = Person.find_by_key!( person_key )
449
+
450
+ timeline = find_record_timeline!( line )
451
+
452
+ laps = find_record_laps!( line )
453
+
454
+ comment = find_record_comment!( line )
455
+
456
+ logger.debug " line2: >#{line}<"
384
457
 
385
458
  record_attribs = {
386
- # pos: pos,
387
- # team_key: team_key # fix: use team_id
459
+ state: state,
460
+ ## team_id: team.id, ## NB: not needed for db
461
+ person_id: person.id,
462
+ timeline: timeline,
463
+ comment: comment,
464
+ laps: laps
388
465
  }
389
466
 
390
- pp record_attribs
467
+ record_attribs = record_attribs.merge( more_attribs )
468
+
469
+ ### check if record exists
470
+ record = Record.find_by_race_id_and_person_id( record_attribs[ :race_id ],
471
+ record_attribs[ :person_id ])
472
+
473
+ if record.present?
474
+ logger.debug "update Record #{record.id}:"
475
+ else
476
+ logger.debug "create Record:"
477
+ record = Record.new
478
+ end
479
+
480
+ logger.debug record_attribs.to_json
481
+
482
+ record.update_attributes!( record_attribs )
483
+
391
484
  end # lines.each
392
485
 
393
486
  end # method load_record_worker
@@ -407,11 +500,15 @@ class Reader
407
500
  # @known_tracks = Track.known_tracks_table
408
501
 
409
502
  ## fix: add @known_teams - for now; use teams (not scoped by event)
503
+ ## for now use all teams
504
+ @known_teams = TextUtils.build_title_table_for( Team.all )
505
+ ## and for now use all persons
506
+ @known_persons = TextUtils.build_title_table_for( Person.all )
507
+
410
508
 
411
509
  load_rosters_worker( reader )
412
510
 
413
- Prop.create_from_fixture!( name, path )
414
-
511
+ Prop.create_from_fixture!( name, path )
415
512
  end
416
513
 
417
514
  def load_rosters_worker( reader )
@@ -419,19 +516,40 @@ class Reader
419
516
  reader.each_line do |line|
420
517
  logger.debug " line: >#{line}<"
421
518
 
422
- ### fix: use new find_leading_pos!
423
- pos = find_game_pos!( line ) # alias -> rename to find_pos! or better use find_leading_pos!( line )
519
+ cut_off_end_of_line_comment!( line )
520
+
521
+ pos = find_leading_pos!( line )
522
+
523
+ map_team!( line )
524
+ team_key = find_team!( line )
525
+ team = Team.find_by_key!( team_key )
526
+
527
+ map_person!( line )
528
+ person_key = find_person!( line )
529
+ person = Person.find_by_key!( person_key )
424
530
 
425
- # match_person!( line )
531
+ logger.debug " line2: >#{line}<"
426
532
 
427
- # match_teams!( line )
533
+ ### check if roster record exists
534
+ roster = Roster.find_by_event_id_and_team_id_and_person_id( @event.id, team.id, person.id )
535
+
536
+ if roster.present?
537
+ logger.debug "update Roster #{roster.id}:"
538
+ else
539
+ logger.debug "create Roster:"
540
+ roster = Roster.new
541
+ end
428
542
 
429
543
  roster_attribs = {
430
- pos: pos,
431
- # team_key: team_key # fix: use team_id
544
+ pos: pos,
545
+ team_id: team.id,
546
+ person_id: person.id,
547
+ event_id: @event.id # NB: reuse/fallthrough from races - make sure load_races goes first (to setup event)
432
548
  }
433
549
 
434
- pp roster_attribs
550
+ logger.debug roster_attribs.to_json
551
+
552
+ roster.update_attributes!( roster_attribs )
435
553
  end # lines.each
436
554
 
437
555
  end # method load_rosters_worker
@@ -439,6 +557,11 @@ class Reader
439
557
 
440
558
 
441
559
  def load_races( name )
560
+ load_event( name ) # must have .yml file with same name for event definition
561
+ @event = fetch_event( name )
562
+
563
+ logger.info " event: #{@event.key} >>#{@event.full_title}<<"
564
+
442
565
  path = "#{include_path}/#{name}.txt"
443
566
 
444
567
  logger.info "parsing data '#{name}' (#{path})..."
@@ -453,28 +576,46 @@ class Reader
453
576
  load_races_worker( reader )
454
577
 
455
578
  Prop.create_from_fixture!( name, path )
456
-
457
579
  end
458
580
 
581
+
459
582
  def load_races_worker( reader )
460
583
 
461
584
  reader.each_line do |line|
462
585
  logger.debug " line: >#{line}<"
463
586
 
464
- ### fix: use new find_leading_pos!
465
- pos = find_game_pos!( line ) # alias -> rename to find_pos! or better use find_leading_pos!( line )
587
+ cut_off_end_of_line_comment!( line )
588
+
589
+ pos = find_leading_pos!( line )
466
590
 
467
- match_track!( line )
591
+ map_track!( line )
468
592
  track_key = find_track!( line )
593
+ track = Track.find_by_key!( track_key )
594
+
469
595
  date = find_date!( line )
470
-
596
+
597
+ logger.debug " line2: >#{line}<"
598
+
599
+ ### check if games exists
600
+ race = Race.find_by_event_id_and_track_id( @event.id, track.id )
601
+
602
+ if race.present?
603
+ logger.debug "update race #{race.id}:"
604
+ else
605
+ logger.debug "create race:"
606
+ race = Race.new
607
+ end
608
+
471
609
  race_attribs = {
472
- pos: pos,
473
- track_key: track_key, # fix: use track_id
474
- start_at: date
610
+ pos: pos,
611
+ track_id: track.id,
612
+ start_at: date,
613
+ event_id: @event.id
475
614
  }
476
615
 
477
- pp race_attribs
616
+ logger.debug race_attribs.to_json
617
+
618
+ race.update_attributes!( race_attribs )
478
619
  end # lines.each
479
620
 
480
621
  end # method load_races_worker
@@ -27,6 +27,7 @@ create_table :persons do |t| # use people ? instead of persons (person/person
27
27
  t.string :key, :null => false # import/export key
28
28
  t.string :name, :null => false
29
29
  t.string :synonyms # comma separated list of synonyms
30
+ t.string :code # three letter code (short title)
30
31
 
31
32
  ## todo: add gender flag (male/female -man/lady how?)
32
33
  t.date :born_at # optional date of birth (birthday)
@@ -61,7 +62,8 @@ end
61
62
  create_table :tracks do |t| # e.g. Formula 1 circuits or Apline Ski resorts/slops/pistes
62
63
  t.string :key, :null => false # import/export key
63
64
  t.string :title, :null => false
64
- t.string :synonyms # comma separated list of synonyms
65
+ t.string :synonyms # comma separated list of synonyms
66
+ t.string :code # three letter code (short title)
65
67
 
66
68
  t.references :city
67
69
  t.references :region
@@ -0,0 +1,146 @@
1
+ # encoding: utf-8
2
+
3
+ ################
4
+ # todo: move module to textutils!!!
5
+
6
+ ### fix: move to textutils??
7
+
8
+
9
+ ## todo: rename to TitleHelpers? TitleMatcher? TitleMapper? TitleMapping? TitleMappings? TitleFinder? TitleHelpers?
10
+ # or rename to KeyMapping?, KeyMapper?, KeyTable? etc.
11
+
12
+
13
+ module TextUtils::TitleTable
14
+
15
+
16
+ def build_title_table_for( records )
17
+ ## build known tracks table w/ synonyms e.g.
18
+ #
19
+ # [[ 'wolfsbrug', [ 'VfL Wolfsburg' ]],
20
+ # [ 'augsburg', [ 'FC Augsburg', 'Augi2', 'Augi3' ]],
21
+ # [ 'stuttgart', [ 'VfB Stuttgart' ]] ]
22
+
23
+ known_titles = []
24
+
25
+ records.each_with_index do |rec,index|
26
+
27
+ title_candidates = []
28
+ title_candidates << rec.title
29
+
30
+ title_candidates += rec.synonyms.split('|') if rec.synonyms.present?
31
+
32
+
33
+ ## check if title includes subtitle e.g. Grand Prix Japan (Suzuka Circuit)
34
+ # make subtitle optional by adding title w/o subtitle e.g. Grand Prix Japan
35
+
36
+ titles = []
37
+ title_candidates.each do |t|
38
+ titles << t
39
+ if t =~ /\(.+\)/
40
+ extra_title = t.gsub( /\(.+\)/, '' ) # remove/delete subtitles
41
+ extra_title.strip! # strip leading n trailing withspaces too!
42
+ titles << extra_title
43
+ end
44
+ end
45
+
46
+
47
+ ## NB: sort here by length (largest goes first - best match)
48
+ # exclude code and key (key should always go last)
49
+ titles = titles.sort { |left,right| right.length <=> left.length }
50
+
51
+ ## escape for regex plus allow subs for special chars/accents
52
+ titles = titles.map { |title| TextUtils.title_esc_regex( title ) }
53
+
54
+ ## NB: only include code field - if defined
55
+ titles << rec.code if rec.respond_to?(:code) && rec.code.present?
56
+
57
+ known_titles << [ rec.key, titles ]
58
+
59
+ ### fix: use plain logger
60
+ LogUtils::Logger.root.debug " #{rec.class.name}[#{index+1}] #{rec.key} >#{titles.join('|')}<"
61
+ end
62
+
63
+ known_titles
64
+ end
65
+
66
+
67
+
68
+ def find_key_for!( name, line )
69
+ regex = /@@oo([^@]+?)oo@@/ # e.g. everything in @@ .... @@ (use non-greedy +? plus all chars but not @, that is [^@])
70
+
71
+ upcase_name = name.upcase
72
+ downcase_name = name.downcase
73
+
74
+ if line =~ regex
75
+ value = "#{$1}"
76
+ ### fix: use plain logger
77
+ LogUtils::Logger.root.debug " #{downcase_name}: >#{value}<"
78
+
79
+ line.sub!( regex, "[#{upcase_name}]" )
80
+
81
+ return $1
82
+ else
83
+ return nil
84
+ end
85
+ end
86
+
87
+
88
+ def find_keys_for!( name, line ) # NB: keys (plural!) - will return array
89
+ counter = 1
90
+ keys = []
91
+
92
+ downcase_name = name.downcase
93
+
94
+ key = find_key_for!( "#{downcase_name}#{counter}", line )
95
+ while key.present?
96
+ keys << key
97
+ counter += 1
98
+ key = find_key_for!( "#{downcase_name}#{counter}", line )
99
+ end
100
+
101
+ keys
102
+ end
103
+
104
+
105
+ def map_titles_for!( name, line, title_table )
106
+ title_table.each do |rec|
107
+ key = rec[0]
108
+ values = rec[1]
109
+ map_title_worker_for!( name, line, key, values )
110
+ end
111
+ end
112
+
113
+
114
+ def map_title_worker_for!( name, line, key, values )
115
+
116
+ downcase_name = name.downcase
117
+
118
+ values.each do |value|
119
+ ## nb: \b does NOT include space or newline for word boundry (only alphanums e.g. a-z0-9)
120
+ ## (thus add it, allows match for Benfica Lis. for example - note . at the end)
121
+
122
+ ## check add $ e.g. (\b| |\t|$) does this work? - check w/ Benfica Lis.$
123
+ regex = /\b#{value}(\b| |\t|$)/ # wrap with world boundry (e.g. match only whole words e.g. not wac in wacker)
124
+ if line =~ regex
125
+ ### fix: use plain logger
126
+ LogUtils::Logger.root.debug " match for #{downcase_name} >#{key}< >#{value}<"
127
+ # make sure @@oo{key}oo@@ doesn't match itself with other key e.g. wacker, wac, etc.
128
+ line.sub!( regex, "@@oo#{key}oo@@ " ) # NB: add one space char at end
129
+ return true # break out after first match (do NOT continue)
130
+ end
131
+ end
132
+ return false
133
+ end
134
+
135
+
136
+
137
+ end # module TextUtils::TitleTable
138
+
139
+
140
+
141
+ ## auto-include methods
142
+
143
+ module TextUtils
144
+ # make helpers available as class methods e.g. TextUtils.convert_unicode_dashes_to_plain_ascii
145
+ extend TitleTable # lets us use TextUtils.build_title_table_for etc.
146
+ end
data/lib/sportdb/utils.rb CHANGED
@@ -1,48 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- ### some utils moved to worldbdb/utils for reuse
4
-
5
-
6
- ### fix: move to textutils??
7
-
8
-
9
- def build_match_table_for( recs )
10
- ## build known tracks table w/ synonyms e.g.
11
- #
12
- # [[ 'wolfsbrug', [ 'VfL Wolfsburg' ]],
13
- # [ 'augsburg', [ 'FC Augsburg', 'Augi2', 'Augi3' ]],
14
- # [ 'stuttgart', [ 'VfB Stuttgart' ]] ]
15
-
16
- known_titles = []
17
-
18
- recs.each_with_index do |rec,index|
19
-
20
- titles = []
21
- titles << rec.title
22
- titles += rec.synonyms.split('|') if rec.synonyms.present?
23
-
24
- ## NB: sort here by length (largest goes first - best match)
25
- # exclude code and key (key should always go last)
26
- titles = titles.sort { |left,right| right.length <=> left.length }
27
-
28
- ## escape for regex plus allow subs for special chars/accents
29
- titles = titles.map { |title| TextUtils.title_esc_regex( title ) }
30
-
31
- ## NB: only include code field - if defined
32
- titles << rec.code if rec.respond_to?(:code) && rec.code.present?
33
-
34
- known_titles << [ rec.key, titles ]
35
-
36
- ### fix:
37
- ## plain logger
38
-
39
- LogUtils::Logger.root.debug " #{rec.class.name}[#{index+1}] #{rec.key} >#{titles.join('|')}<"
40
- end
41
-
42
- known_titles
43
- end
44
-
45
-
3
+ ### note: some utils moved to worldbdb/utils for reuse
46
4
 
47
5
 
48
6
  module SportDb::FixtureHelpers
@@ -116,11 +74,16 @@ module SportDb::FixtureHelpers
116
74
  return [title,pos]
117
75
  end
118
76
 
77
+
119
78
  def cut_off_end_of_line_comment!( line )
120
79
  # cut off (that is, remove) optional end of line comment starting w/ #
121
80
 
122
- line = line.sub( /#.*$/, '' )
123
- line
81
+ line.sub!( /#.*$/ ) do |_|
82
+ logger.debug " cutting off end of line comment - >>#{$&}<<"
83
+ ''
84
+ end
85
+
86
+ # NB: line = line.sub will NOT work - thus, lets use line.sub!
124
87
  end
125
88
 
126
89
 
@@ -204,15 +167,21 @@ module SportDb::FixtureHelpers
204
167
  # nb: allow 2.3.2012 e.g. no leading zero required
205
168
  # nb: allow hour as 20.30 or 3.30 instead of 03.30
206
169
  regex_de = /\b(\d{1,2})\.(\d{1,2})\.\s+(\d{1,2})[:.](\d{2})\b/
207
-
170
+
208
171
  # e.g. 14.09.2012 20:30 => DD.MM.YYYY HH:MM
209
172
  # nb: allow 2.3.2012 e.g. no leading zero required
210
173
  # nb: allow hour as 20.30
211
174
  regex_de2 = /\b(\d{1,2})\.(\d{1,2})\.(\d{4})\s+(\d{1,2})[:.](\d{2})\b/
212
175
 
213
176
 
177
+ month_abbrev_en = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"
178
+
179
+ # e.g. 12 May 2013 14:00 => D|DD.MMM.YYYY H|HH:MM
180
+ regex_en = /\b(\d{1,2})\s(#{month_abbrev_en})\s(\d{4})\s+(\d{1,2}):(\d{2})\b/
181
+
182
+
214
183
  if line =~ regex_db
215
- value = "#{$1}-#{$2}-#{$3} #{$4}:#{$5}"
184
+ value = '%d-%02d-%02d %02d:%02d' % [$1, $2, $3, $4, $5]
216
185
  logger.debug " date: >#{value}<"
217
186
 
218
187
  ## todo: lets you configure year
@@ -222,14 +191,14 @@ module SportDb::FixtureHelpers
222
191
 
223
192
  return DateTime.strptime( value, '%Y-%m-%d %H:%M' )
224
193
  elsif line =~ regex_db2
225
- value = "#{$1}-#{$2}-#{$3} 12:00"
194
+ value = '%d-%02d-%02d 12:00' % [$1, $2, $3]
226
195
  logger.debug " date: >#{value}<"
227
-
196
+
228
197
  line.sub!( regex_db2, '[DATE.DB2]' )
229
198
 
230
199
  return DateTime.strptime( value, '%Y-%m-%d %H:%M' )
231
200
  elsif line =~ regex_de2
232
- value = "#{$3}-#{$2}-#{$1} #{$4}:#{$5}"
201
+ value = '%d-%02d-%02d %02d:%02d' % [$3, $2, $1, $4, $5]
233
202
  logger.debug " date: >#{value}<"
234
203
 
235
204
  ## todo: lets you configure year
@@ -243,8 +212,8 @@ module SportDb::FixtureHelpers
243
212
  #### fix/todo:
244
213
  # get year from event start date!!!!
245
214
  # do NOT hard code!!!!
246
-
247
- value = "2012-#{$2}-#{$1} #{$3}:#{$4}"
215
+
216
+ value = '2012-%02d-%02d %02d:%02d' % [$2, $1, $3, $4]
248
217
  logger.debug " date: >#{value}<"
249
218
 
250
219
  ## todo: lets you configure year
@@ -253,13 +222,103 @@ module SportDb::FixtureHelpers
253
222
  line.sub!( regex_de, '[DATE.DE]' )
254
223
 
255
224
  return DateTime.strptime( value, '%Y-%m-%d %H:%M' )
225
+ elsif line =~ regex_en
226
+ value = '%d-%s-%02d %02d:%02d' % [$3, $2, $1, $4, $5]
227
+ logger.debug " date: >#{value}<"
228
+
229
+ line.sub!( regex_en, '[DATE.EN]' )
230
+
231
+ return DateTime.strptime( value, '%Y-%b-%d %H:%M' ) ## %b - abbreviated month name (e.g. Jan,Feb, etc.)
256
232
  else
257
233
  return nil
258
234
  end
259
235
  end
260
236
 
261
237
 
262
- def find_game_pos!( line )
238
+ def find_record_comment!( line )
239
+ # assume everything left after the last record marker,that is, ] is a record comment
240
+
241
+ regex = /]([^\]]+?)$/ # NB: use non-greedy +?
242
+
243
+ if line =~ regex
244
+ value = $1.strip
245
+ return nil if value.blank? # skip whitespaces only
246
+
247
+ logger.debug " comment: >#{value}<"
248
+
249
+ line.sub!( value, '[REC.COMMENT] ' )
250
+ return value
251
+ else
252
+ return nil
253
+ end
254
+ end
255
+
256
+
257
+ def find_record_timeline!( line )
258
+
259
+ # +1 lap or +n laps
260
+ regex_laps = /\s+\+\d{1,2}\s(lap|laps)\b/
261
+
262
+ # 2:17:15.123
263
+ regex_time = /\b\d{1,2}:\d{2}:\d{2}\.\d{1,3}\b/
264
+
265
+ # +40.1 secs
266
+ regex_secs = /\s+\+\d{1,3}\.\d{1,3}\s(secs)\b/ # NB: before \+ - boundry (\b) will not work
267
+
268
+ # NB: $& contains the complete matched text
269
+
270
+ if line =~ regex_laps
271
+ value = $&.strip
272
+ logger.debug " timeline.laps: >#{value}<"
273
+
274
+ line.sub!( value, '[REC.TIMELINE.LAPS] ' ) # NB: add trailing space
275
+ return value
276
+ elsif line =~ regex_time
277
+ value = $&.strip
278
+ logger.debug " timeline.time: >#{value}<"
279
+
280
+ line.sub!( value, '[REC.TIMELINE.TIME] ' ) # NB: add trailing space
281
+ return value
282
+ elsif line =~ regex_secs
283
+ value = $&.strip
284
+ logger.debug " timeline.secs: >#{value}<"
285
+
286
+ line.sub!( value, '[REC.TIMELINE.SECS] ' ) # NB: add trailing space
287
+ return value
288
+ else
289
+ return nil
290
+ end
291
+ end
292
+
293
+ def find_record_laps!( line )
294
+ # e.g. first free-standing number w/ one or two digits e.g. 7 or 28 etc.
295
+ regex = /\b(\d{1,2})\b/
296
+ if line =~ regex
297
+ logger.debug " laps: >#{$1}<"
298
+
299
+ line.sub!( regex, '[REC.LAPS] ' ) # NB: add trailing space
300
+ return $1.to_i
301
+ else
302
+ return nil
303
+ end
304
+ end
305
+
306
+ def find_record_leading_state!( line )
307
+ # e.g. 1|2|3|etc or Ret - must start line
308
+ regex = /^[ \t]*(\d{1,3}|Ret)[ \t]+/
309
+ if line =~ regex
310
+ value = $1.dup
311
+ logger.debug " state: >#{value}<"
312
+
313
+ line.sub!( regex, '[REC.STATE] ' ) # NB: add trailing space
314
+ return value
315
+ else
316
+ return nil
317
+ end
318
+ end
319
+
320
+
321
+ def find_leading_pos!( line )
263
322
  # extract optional game pos from line
264
323
  # and return it
265
324
  # NB: side effect - removes pos from line string
@@ -268,13 +327,17 @@ module SportDb::FixtureHelpers
268
327
  regex = /^[ \t]*\((\d{1,3})\)[ \t]+/
269
328
  if line =~ regex
270
329
  logger.debug " pos: >#{$1}<"
271
-
272
- line.sub!( regex, '[POS] ' )
330
+
331
+ line.sub!( regex, '[POS] ' ) # NB: add trailing space
273
332
  return $1.to_i
274
333
  else
275
334
  return nil
276
335
  end
336
+ end
277
337
 
338
+ def find_game_pos!( line )
339
+ ## fix: add depreciation warning - remove - use find_leading_pos!
340
+ find_leading_pos!( line )
278
341
  end
279
342
 
280
343
  def find_scores!( line )
@@ -330,99 +393,62 @@ module SportDb::FixtureHelpers
330
393
  end # methdod find_scores!
331
394
 
332
395
 
333
- ## todo/fix:
334
- # find a better name find_xxx_by_title ?? find_xxx_w_match_table? or similiar
335
- # move to its own file/module for easier maintance
336
- # include build_match_table_for
337
- # - lets us change internals e.g. lets improve matcher using a reverse index, for example
338
-
339
- def find_xxx_worker!( name, line )
340
- regex = /@@oo([^@]+?)oo@@/ # e.g. everything in @@ .... @@ (use non-greedy +? plus all chars but not @, that is [^@])
341
-
342
- upcase_name = name.upcase
343
- downcase_name = name.downcase
344
-
345
- if line =~ regex
346
- value = "#{$1}"
347
- logger.debug " #{downcase_name}: >#{value}<"
348
-
349
- line.sub!( regex, "[#{upcase_name}]" )
350
-
351
- return $1
352
- else
353
- return nil
354
- end
355
- end
356
-
357
-
358
- def match_xxx_worker!( name, line, key, values )
359
-
360
- downcase_name = name.downcase
361
396
 
362
- values.each do |value|
363
- ## nb: \b does NOT include space or newline for word boundry (only alphanums e.g. a-z0-9)
364
- ## (thus add it, allows match for Benfica Lis. for example - note . at the end)
365
-
366
- ## check add $ e.g. (\b| |\t|$) does this work? - check w/ Benfica Lis.$
367
- regex = /\b#{value}(\b| |\t|$)/ # wrap with world boundry (e.g. match only whole words e.g. not wac in wacker)
368
- if line =~ regex
369
- logger.debug " match for #{downcase_name} >#{key}< >#{value}<"
370
- # make sure @@oo{key}oo@@ doesn't match itself with other key e.g. wacker, wac, etc.
371
- line.sub!( regex, "@@oo#{key}oo@@ " ) # NB: add one space char at end
372
- return true # break out after first match (do NOT continue)
373
- end
374
- end
375
- return false
397
+ def find_teams!( line ) # NB: returns an array - note: plural! (teamsss)
398
+ TextUtils.find_keys_for!( 'team', line )
376
399
  end
377
-
378
-
379
-
380
- def find_teams!( line )
381
- counter = 1
382
- teams = []
383
-
384
- team = find_xxx_worker!( "team#{counter}", line )
385
- while team.present?
386
- teams << team
387
- counter += 1
388
- team = find_xxx_worker!( "team#{counter}", line )
389
- end
390
-
391
- teams
400
+
401
+ def find_team!( line ) # NB: returns key (string or nil)
402
+ TextUtils.find_key_for!( 'team', line )
392
403
  end
393
404
 
394
405
  ## todo: check if find_team1 gets used? if not remove it!! use find_teams!
395
406
  def find_team1!( line )
396
- find_xxx_worker!( 'team1', line )
407
+ TextUtils.find_key_for!( 'team1', line )
397
408
  end
398
-
409
+
399
410
  def find_team2!( line )
400
- find_xxx_worker!( 'team2', line )
411
+ TextUtils.find_key_for!( 'team2', line )
401
412
  end
402
413
 
403
-
404
414
  ## todo/fix: pass in known_teams as a parameter? why? why not?
405
- def match_teams!( line )
406
- @known_teams.each do |rec|
407
- key = rec[0]
408
- values = rec[1]
409
- match_xxx_worker!( 'team', line, key, values )
410
- end # each known_teams
411
- end # method match_teams!
412
-
413
415
 
416
+ def map_teams!( line )
417
+ TextUtils.map_titles_for!( 'team', line, @known_teams )
418
+ end
419
+
420
+ def map_team!( line ) # alias map_teams!
421
+ map_teams!( line )
422
+ end
414
423
 
415
424
  def find_track!( line )
416
- find_xxx_worker!( 'track', line )
425
+ TextUtils.find_key_for!( 'track', line )
417
426
  end
418
427
 
419
428
  ## todo/fix: pass in known_tracks as a parameter? why? why not?
420
- def match_track!( line )
421
- @known_tracks.each do |rec|
422
- key = rec[0]
423
- values = rec[1]
424
- match_xxx_worker!( 'track', line, key, values )
425
- end # each known_tracks
429
+ def map_track!( line )
430
+ TextUtils.map_titles_for!( 'track', line, @known_tracks )
431
+ end
432
+
433
+ def find_person!( line )
434
+ TextUtils.find_key_for!( 'person', line )
435
+ end
436
+
437
+ def map_person!( line )
438
+ TextUtils.map_titles_for!( 'person', line, @known_persons)
439
+ end
440
+
441
+
442
+
443
+ ## depreciated methods - use map_
444
+ def match_teams!( line ) ## fix: rename to map_teams!! - remove match_teams!
445
+ ## todo: issue depreciated warning
446
+ map_teams!( line )
447
+ end # method match_teams!
448
+
449
+ def match_track!( line ) ## fix: rename to map_track!!!
450
+ ## todo: issue depreciated warning
451
+ map_track!( line )
426
452
  end # method match_tracks!
427
453
 
428
454
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  module SportDb
3
- VERSION = '1.6.12'
3
+ VERSION = '1.6.13'
4
4
  end
5
5
 
6
6
  ###########################################
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.12
4
+ version: 1.6.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2013-06-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: worlddb
16
- requirement: &77877370 !ruby/object:Gem::Requirement
16
+ requirement: &72775370 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.7'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *77877370
24
+ version_requirements: *72775370
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: commander
27
- requirement: &77877150 !ruby/object:Gem::Requirement
27
+ requirement: &72775150 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 4.1.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *77877150
35
+ version_requirements: *72775150
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rdoc
38
- requirement: &77876930 !ruby/object:Gem::Requirement
38
+ requirement: &72774930 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.10'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *77876930
46
+ version_requirements: *72774930
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: hoe
49
- requirement: &77876710 !ruby/object:Gem::Requirement
49
+ requirement: &72774710 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.3'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *77876710
57
+ version_requirements: *72774710
58
58
  description: sportdb - sport.db command line tool
59
59
  email: opensport@googlegroups.com
60
60
  executables:
@@ -107,6 +107,7 @@ files:
107
107
  - lib/sportdb/reader.rb
108
108
  - lib/sportdb/schema.rb
109
109
  - lib/sportdb/stats.rb
110
+ - lib/sportdb/title.rb
110
111
  - lib/sportdb/utils.rb
111
112
  - lib/sportdb/version.rb
112
113
  - tasks/test.rb