sportdb-readers 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4a900ae50e920da9b842d72e975577d098dbedd
4
- data.tar.gz: 9576ab392dcadb0d672535d59614c7d93ed29387
3
+ metadata.gz: 976e1c02d5e34f69d999581d0036b1dce641d27b
4
+ data.tar.gz: f84742df381b09fa244d963d6624d5a0137c3159
5
5
  SHA512:
6
- metadata.gz: e1585ce4281e1725b0452194c1c34858714dc5a40ed4b25ffe598182be9878ce46f544ee66b3d43dc64ca5480b3e00195ae6c08de013a4f3c26962c1cb920484
7
- data.tar.gz: fd04ee65cde3a82a144473b1d63eb8a2cba1cf63c5dc0d4e0a98122ece942f2bef980ef751a6752273acf01c7adaaab68e5202d35bc86c8dc09356dde39b1b84
6
+ metadata.gz: 040b202ef7acac2ce56a67f33b88c9c93fe961173cfb5ff28983dee540b643963ed5581273be2d6ee5c6f8d257f1559b62f7ea5c340fc8b02c7c279768add7f7
7
+ data.tar.gz: d0f3cfa4c66a261f5d4a30b24e7e5f86aef8ebc78730dd4782ffc33d00d44205e5d23a44275640664851fb0e7e10f83b5cc1a789293031c6b638dcf53de36b18
data/Manifest.txt CHANGED
@@ -1,17 +1,19 @@
1
1
  CHANGELOG.md
2
2
  Manifest.txt
3
+ NOTES.md
3
4
  README.md
4
5
  Rakefile
5
6
  lib/sportdb/readers.rb
6
- lib/sportdb/readers/conf_linter.rb
7
7
  lib/sportdb/readers/conf_reader.rb
8
- lib/sportdb/readers/league_outline_reader.rb
9
- lib/sportdb/readers/match_linter.rb
10
8
  lib/sportdb/readers/match_reader.rb
11
9
  lib/sportdb/readers/package.rb
12
10
  lib/sportdb/readers/version.rb
13
11
  test/helper.rb
12
+ test/test_conf_reader.rb
13
+ test/test_match_reader_champs.rb
14
14
  test/test_match_reader_eng.rb
15
+ test/test_match_reader_euro.rb
15
16
  test/test_match_reader_mu.rb
16
17
  test/test_read.rb
17
18
  test/test_reader.rb
19
+ test/test_reader_champs.rb
data/NOTES.md ADDED
@@ -0,0 +1,35 @@
1
+ # Notes
2
+
3
+ ## Todos
4
+
5
+
6
+ todo/fix: use config / configurations; remove all globals e.g.:
7
+ - COUNTRIES
8
+ - CLUBS
9
+ - LEAGUES !!!!!
10
+
11
+
12
+
13
+ fix/todo: move find_league to sportdb-league index use find_by! and find_by !!!!
14
+ in sportdb-readers (and others?)
15
+
16
+ ```
17
+ def self.find_league( name )
18
+ league = nil
19
+ m = config.leagues.match( name )
20
+ # pp m
21
+
22
+ if m.nil?
23
+ puts "** !!! ERROR !!! no league match found for >#{name}<, add to leagues table; sorry"
24
+ exit 1
25
+ elsif m.size > 1
26
+ puts "** !!! ERROR !!! ambigious league name; too many leagues (#{m.size}) found:"
27
+ pp m
28
+ exit 1
29
+ else
30
+ league = m[0]
31
+ end
32
+
33
+ league
34
+ end
35
+ ```
data/Rakefile CHANGED
@@ -20,9 +20,7 @@ Hoe.spec 'sportdb-readers' do
20
20
  self.licenses = ['Public Domain']
21
21
 
22
22
  self.extra_deps = [
23
- ['sportdb-config', '>= 0.9.0'],
24
- ['sportdb-models', '>= 1.18.6'],
25
- ['sportdb-sync', '>= 0.1.0'],
23
+ ['sportdb-sync', '>= 1.0.0'],
26
24
  ]
27
25
 
28
26
  self.spec_extras = {
@@ -1,19 +1,13 @@
1
1
  # encoding: utf-8
2
2
 
3
-
4
- require 'sportdb/config'
5
- require 'sportdb/models' ## add sql database support
6
3
  require 'sportdb/sync'
7
4
 
8
5
 
9
6
  ###
10
7
  # our own code
11
8
  require 'sportdb/readers/version' # let version always go first
12
- require 'sportdb/readers/league_outline_reader'
13
9
  require 'sportdb/readers/conf_reader'
14
- require 'sportdb/readers/conf_linter'
15
10
  require 'sportdb/readers/match_reader'
16
- require 'sportdb/readers/match_linter'
17
11
  require 'sportdb/readers/package'
18
12
 
19
13
 
@@ -22,76 +16,43 @@ require 'sportdb/readers/package'
22
16
  ##
23
17
  ## add convenience shortcut helpers
24
18
  module SportDb
19
+ def self.read_conf( path, season: nil ) ConfReaderV2.read( path, season: season ); end
20
+ def self.parse_conf( txt, season: nil ) ConfReaderV2.parse( txt, season: season ); end
21
+
22
+ ### todo/check: add alias read_matches - why? why not?
23
+ def self.read_match( path, season: nil ) MatchReaderV2.read( path, season: season ); end
24
+ def self.parse_match( txt, season: nil ) MatchReaderV2.parse( txt, season: season ); end
25
+
26
+ def self.read_club_props( path ) Import::ClubPropsReader.read( path ); end
27
+ def self.parse_club_props( txt ) Import::ClubPropsReader.parse( txt ); end
28
+
29
+ def self.parse_leagues( txt ) recs = Import::LeagueReader.parse( txt ); Import::config.leagues.add( recs ); end
30
+ def self.parse_clubs( txt ) recs = Import::ClubReader.parse( txt ); Import::config.clubs.add( recs ); end
25
31
 
26
- ## note: sync is dry run (for lint checking)
27
- def self.read_conf( path, season: nil, sync: true )
28
- sync ? ConfReaderV2.read( path, season: season )
29
- : ConfLinter.read( path, season: season )
30
- end
31
- def self.parse_conf( txt, season: nil, sync: true )
32
- sync ? ConfReaderV2.parse( txt, season: season )
33
- : ConfLinter.parse( txt, season: season )
34
- end
35
-
36
- def self.read_match( path, season: nil, sync: true ) ### todo/check: add alias read_matches - why? why not?
37
- sync ? MatchReaderV2.read( path, season: season )
38
- : MatchLinter.read( path, season: season )
39
- end
40
- def self.parse_match( txt, season: nil, sync: true ) ### todo/check: add alias read_matches - why? why not?
41
- sync ? MatchReaderV2.parse( txt, season: season )
42
- : MatchLinter.parse( txt, season: season )
43
- end
44
-
45
- def self.read_club_props( path, sync: true )
46
- ## note: for now run only if sync (e.g. run with db updates)
47
- SportDb::Import::ClubPropsReader.read( path ) if sync
48
- end
49
- def self.parse_club_props( txt, sync: true )
50
- ## note: for now run only if sync (e.g. run with db updates)
51
- SportDb::Import::ClubPropsReader.parse( txt ) if sync
52
- end
53
-
54
-
55
- def self.parse_leagues( txt )
56
- recs = SportDb::Import::LeagueReader.parse( txt )
57
- Import::config.leagues.add( recs )
58
- end
59
-
60
- def self.parse_clubs( txt )
61
- recs = SportDb::Import::ClubReader.parse( txt )
62
- Import::config.clubs.add( recs )
63
- end
64
-
65
-
66
- def self.read( path, season: nil, sync: true )
32
+
33
+ def self.read( path, season: nil )
67
34
  pack = if File.directory?( path ) ## if directory assume "unzipped" package
68
35
  DirPackage.new( path )
69
- elsif File.file?( path ) && Datafile.match_zip( path ) ## check if file is a .zip (archive) file
36
+ elsif File.file?( path ) && File.extname( path ) == '.zip' ## check if file is a .zip (archive) file
70
37
  ZipPackage.new( path )
71
38
  else ## no package; assume single (standalone) datafile
72
39
  nil
73
40
  end
74
41
 
75
42
  if pack
76
- pack.read( season: season, sync: sync )
43
+ pack.read( season: season )
77
44
  else
78
- if Datafile.match_conf( path ) ## check if datafile matches conf(iguration) naming (e.g. .conf.txt)
79
- read_conf( path, season: season, sync: sync )
80
- elsif Datafile.match_club_props( path )
81
- read_club_props( path, sync: sync )
45
+ if Package.conf?( path ) ## check if datafile matches conf(iguration) naming (e.g. .conf.txt)
46
+ read_conf( path, season: season )
47
+ elsif Package.club_props?( path )
48
+ read_club_props( path )
82
49
  else ## assume "regular" match datafile
83
- read_match( path, season: season, sync: sync )
50
+ read_match( path, season: season )
84
51
  end
85
52
  end
86
53
  end # method read
87
54
 
88
55
 
89
-
90
- ## (more) convenience helpers for lint(ing)
91
- def self.lint( path, season: nil ) read( path, season: season, sync: false ); end
92
- def self.lint_conf( path, season: nil ) read_conf( path, season: season, sync: false ); end
93
- def self.lint_match( path, season: nil ) read_match( path, season: season, sync: false ); end
94
-
95
56
  end # module SportDb
96
57
 
97
58
 
@@ -5,84 +5,96 @@ module SportDb
5
5
 
6
6
  class ConfReaderV2 ## todo/check: rename to EventsReaderV2 (use plural?) why? why not?
7
7
 
8
- def self.config() Import.config; end ## shortcut convenience helper
9
-
10
-
11
8
  def self.read( path, season: nil ) ## use - rename to read_file or from_file etc. - why? why not?
12
- txt = File.open( path, 'r:utf-8' ).read
9
+ txt = File.open( path, 'r:utf-8' ) {|f| f.read }
13
10
  parse( txt, season: season )
14
11
  end
15
12
 
16
13
  def self.parse( txt, season: nil )
17
- recs = LeagueOutlineReader.parse( txt, season: season )
18
- pp recs
19
-
20
- ## pass 2 - check & map; replace inline (string with record)
21
- recs.each do |rec|
22
- league = rec[:league]
23
- clubs = [] ## convert lines to clubs
24
- rec[:lines].each do |line|
25
-
26
- next if line =~ /^[ -]+$/ ## skip decorative lines with dash only (e.g. ---- or - - - -) etc.
27
-
28
- scan = StringScanner.new( line )
29
-
30
- if scan.check( /\d{1,2}[ ]+/ ) ## entry with standaning starts with ranking e.g. 1,2,3, etc.
31
- puts " table entry >#{line}<"
32
- rank = scan.scan( /\d{1,2}[ ]+/ ).strip # note: strip trailing spaces
33
-
34
- ## note: uses look ahead scan until we hit at least two spaces
35
- ## or the end of string (standing records for now optional)
36
- name = scan.scan_until( /(?=\s{2})|$/ )
37
- if scan.eos?
38
- standing = nil
39
- else
40
- standing = scan.rest.strip # note: strip leading and trailing spaces
41
- end
42
- puts " rank: >#{rank}<, name: >#{name}<, standing: >#{standing}<"
14
+ new( txt ).parse( season: season )
15
+ end
43
16
 
44
- ## note: rank and standing gets ignored (not used) for now
17
+
18
+ include Logging
19
+
20
+ def initialize( txt )
21
+ @txt = txt
22
+ end
23
+
24
+ def parse( season: nil )
25
+ secs = LeagueOutlineReader.parse( @txt, season: season )
26
+ pp secs
27
+
28
+ ## pass 1 - check & map; replace inline (string with record)
29
+ secs.each do |sec| # sec(tion)s
30
+
31
+ conf = ConfParser.parse( sec[:lines] )
32
+
33
+ league = sec[:league]
34
+ teams = [] ## convert lines to teams
35
+
36
+ if league.clubs?
37
+ if league.intl?
38
+ conf.each do |name, rec|
39
+ country_key = rec[:country]
40
+ teams << catalog.clubs.find_by!( name: name,
41
+ country: country_key )
42
+ end
45
43
  else
46
- ## assume club is full line
47
- name = line
44
+ conf.each do |name, _|
45
+ ## note: rank and standing gets ignored (not used) for now
46
+ teams << catalog.clubs.find_by!( name: name,
47
+ country: league.country )
48
+ end
49
+ end
50
+ else ### assume national teams
51
+ conf.each do |name, _|
52
+ ## note: rank and standing gets ignored (not used) for now
53
+ teams << catalog.national_teams.find!( name )
48
54
  end
49
-
50
- clubs << config.clubs.find_by( name: name, country: league.country )
51
55
  end
52
56
 
53
- rec[:clubs] = clubs
54
- rec.delete( :lines ) ## remove lines entry
57
+
58
+ sec[:teams] = teams
59
+
60
+ sec.delete( :lines ) ## remove lines entry
55
61
  end
56
62
 
57
- ## pass 3 - import (insert/update) into db
58
- recs.each do |rec|
59
- league = Sync::League.find_or_create( rec[:league] )
60
- season = Sync::Season.find_or_create( rec[:season] )
61
63
 
64
+ ## pass 2 - import (insert/update) into db
65
+ secs.each do |sec| # sec(tion)s
66
+ ## todo/fix: always return Season struct record in LeagueReader - why? why not?
67
+ event_rec = Sync::Event.find_or_create_by( league: sec[:league],
68
+ season: sec[:season] )
62
69
 
63
- event = Sync::Event.find_or_create( league: league, season: season )
64
- if rec[:stage]
65
- stage = Sync::Stage.find_or_create( rec[:stage], event: event )
66
- else
67
- stage = nil
68
- end
70
+ stage_rec = if sec[:stage]
71
+ Sync::Stage.find_or_create( sec[:stage], event: event_rec )
72
+ else
73
+ nil
74
+ end
69
75
 
76
+ ## todo/fix: check if all teams are unique
77
+ ## check if uniq works for club/national_team record (struct) - yes,no ??
78
+ teams = sec[:teams]
79
+ teams = teams.uniq
70
80
 
71
- rec[:clubs].each do |club_rec|
72
- club = Sync::Club.find_or_create( club_rec )
81
+ ## add to database
82
+ team_recs = stage_rec ? stage_rec.teams : event_rec.teams
83
+ team_ids = stage_rec ? stage_rec.team_ids : event_rec.team_ids
84
+
85
+ new_team_recs = Sync::Team.find_or_create( teams )
86
+ new_team_recs.each do |team_rec|
73
87
  ## add teams to event
74
- ## todo/fix: check if team is alreay included?
75
- ## or clear/destroy_all first!!!
76
- if stage
77
- stage.teams << club
78
- else
79
- event.teams << club
80
- end
88
+ ## for now check if team is alreay included
89
+ ## todo/fix: clear/destroy_all first - why? why not!!!
90
+ team_recs << team_rec unless team_ids.include?( team_rec.id )
81
91
  end
82
92
  end
83
93
 
84
- recs
94
+ true ## todo/fix: return true/false or something
85
95
  end # method read
86
96
 
97
+ def catalog() Import.catalog; end ## shortcut convenience helper
98
+
87
99
  end # class ConfReaderV2
88
100
  end # module SportDb
@@ -4,118 +4,150 @@ module SportDb
4
4
 
5
5
  class MatchReaderV2 ## todo/check: rename to MatchReaderV2 (use plural?) why? why not?
6
6
 
7
- def self.config() Import.config; end
8
-
9
-
10
-
11
7
  def self.read( path, season: nil ) ## use - rename to read_file or from_file etc. - why? why not?
12
- txt = File.open( path, 'r:utf-8' ).read
8
+ txt = File.open( path, 'r:utf-8' ) {|f| f.read }
13
9
  parse( txt, season: season )
14
10
  end
15
11
 
16
12
  def self.parse( txt, season: nil )
17
- recs = LeagueOutlineReader.parse( txt, season: season )
18
- pp recs
19
-
20
- recs.each do |rec|
21
- league = Sync::League.find_or_create( rec[:league] )
22
- season = Sync::Season.find_or_create( rec[:season] )
13
+ new( txt ).parse( season: season )
14
+ end
23
15
 
24
- ## hack for now: switch lang
25
- if ['de', 'at'].include?( league.country.key )
26
- SportDb.lang.lang = 'de'
27
- DateFormats.lang = 'de'
28
- elsif ['fr'].include?( league.country.key )
29
- SportDb.lang.lang = 'fr'
30
- DateFormats.lang = 'fr'
31
- elsif ['it'].include?( league.country.key )
32
- SportDb.lang.lang = 'it'
33
- DateFormats.lang = 'it'
34
- elsif ['es', 'mx'].include?( league.country.key )
35
- SportDb.lang.lang = 'es'
36
- DateFormats.lang = 'es'
37
- elsif ['pt', 'br'].include?( league.country.key )
38
- SportDb.lang.lang = 'pt'
39
- DateFormats.lang = 'pt'
40
- else
41
- SportDb.lang.lang = 'en'
42
- DateFormats.lang = 'en'
43
- end
44
16
 
17
+ include Logging
45
18
 
46
- ## todo/fix:
47
- ## always auto create
48
- ## 1) check for clubs count on event/stage - only if count == 0 use autoconf!!!
49
- ## 2) add lang switch for date/lang too!!!!
19
+ def initialize( txt )
20
+ @txt = txt
21
+ end
50
22
 
51
- event = Sync::Event.find_or_create( league: league, season: season )
23
+ def parse( season: nil )
24
+ secs = LeagueOutlineReader.parse( @txt, season: season )
25
+ pp secs
26
+
27
+
28
+ ###
29
+ ## todo/check/fix: move to LeagueOutlineReader for (re)use - why? why not?
30
+ ## use sec[:lang] or something?
31
+ langs = { ## map country keys to lang codes
32
+ 'de' => 'de', ## de - Deutsch (German)
33
+ 'at' => 'de',
34
+ 'fr' => 'fr', ## fr - French
35
+ 'it' => 'it', ## it - Italian
36
+ 'es' => 'es', ## es - Español (Spanish)
37
+ 'mx' => 'es',
38
+ 'pt' => 'pt', ## pt - Português (Portuguese)
39
+ 'br' => 'br'
40
+ }
41
+
42
+ secs.each do |sec| ## sec(tion)s
43
+ season = sec[:season]
44
+ league = sec[:league]
45
+ stage = sec[:stage]
46
+ lines = sec[:lines]
52
47
 
53
- stage = if rec[:stage]
54
- Sync::Stage.find_or_create( rec[:stage], event: event )
55
- else
56
- nil
48
+ ## hack for now: switch lang
49
+ ## todo/fix: set lang for now depending on league country!!!
50
+ if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
51
+ Import.config.lang = 'en'
52
+ else ## assume national/domestic
53
+ Import.config.lang = langs[ league.country.key ] || 'en'
57
54
  end
58
55
 
59
56
 
60
- auto_conf_clubs, _ = AutoConfParser.parse( rec[:lines],
61
- start: event.start_at )
57
+ start = if season.year?
58
+ Date.new( season.start_year, 1, 1 )
59
+ else
60
+ Date.new( season.start_year, 7, 1 )
61
+ end
62
+
63
+ auto_conf_teams, _ = AutoConfParser.parse( lines,
64
+ start: start )
62
65
 
63
- ## step 1: map/find clubs
64
- club_recs = [] ## array of struct records
65
- club_mapping = {} ## name => database (ActiveRecord) record
66
+ ## step 1: map/find teams
66
67
 
67
68
  ## note: loop over keys (holding the names); values hold the usage counter!! e.g. 'Arsenal' => 2, etc.
68
- country = league.country
69
- auto_conf_clubs.keys.each do |name|
70
- club_rec = config.clubs.find_by!( name: name, country: country )
71
- club_recs << club_rec
69
+ mods = nil
70
+ if league.clubs? && league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
71
+ ### quick hack mods for popular/known ambigious club names
72
+ ## todo/fix: make more generic / reuseable!!!!
73
+ mods = {}
74
+ ## europa league uses same mods as champions league
75
+ mods[ 'uefa.el' ] = mods[ 'uefa.cl' ] = catalog.clubs.build_mods(
76
+ { 'Liverpool | Liverpool FC' => 'Liverpool FC, ENG',
77
+ 'Arsenal | Arsenal FC' => 'Arsenal FC, ENG',
78
+ 'Barcelona' => 'FC Barcelona, ESP',
79
+ 'Valencia' => 'Valencia CF, ESP' })
80
+ end
72
81
 
73
- club = Sync::Club.find_or_create( club_rec )
74
- club_mapping[ name ] = club
75
- end
82
+ teams = catalog.teams.find_by!( name: auto_conf_teams.keys,
83
+ league: league,
84
+ mods: mods )
76
85
 
86
+ ## build mapping - name => team struct record
87
+ team_mapping = auto_conf_teams.keys.zip( teams ).to_h
77
88
 
78
- ## todo/fix: check if all clubs are unique
79
- ## check if uniq works for club record (struct) - yes,no ??
80
- clubs = club_mapping.values.uniq
81
89
 
90
+ parser = MatchParserSimpleV2.new( lines,
91
+ team_mapping,
92
+ start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
82
93
 
83
- ## step 2: add to database
84
- teams = stage ? stage.teams : event.teams
85
- team_ids = stage ? stage.team_ids : event.team_ids
94
+ matches, rounds, groups = parser.parse
86
95
 
87
- clubs.each do |club|
88
- ## add teams to event
89
- ## for now check if team is alreay included
90
- ## todo/fix: clear/destroy_all first - why? why not!!!
96
+ pp rounds
97
+ pp groups
91
98
 
92
- teams << club unless team_ids.include?( club.id )
93
- end
94
99
 
100
+ ######################################################
101
+ ## step 2: add to database
95
102
 
103
+ event_rec = Sync::Event.find_or_create_by( league: league,
104
+ season: season )
96
105
 
97
- ## todo/fix: set lang for now depending on league country!!!
98
- parser = MatchParserSimpleV2.new( rec[:lines],
99
- club_mapping,
100
- event.start_at ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
106
+ stage_rec = if stage
107
+ Sync::Stage.find_or_create( stage, event: event_rec )
108
+ else
109
+ nil
110
+ end
111
+
112
+ team_recs = stage_rec ? stage_rec.teams : event_rec.teams
113
+ team_ids = stage_rec ? stage_rec.team_ids : event_rec.team_ids
114
+
115
+ ## todo/fix: check if all teams are unique
116
+ ## check if uniq works for club record (struct) - yes,no ??
117
+ new_team_recs = Sync::Team.find_or_create( team_mapping.values.uniq )
101
118
 
102
- match_recs, round_recs = parser.parse
119
+ new_team_recs.each do |team_rec|
120
+ ## add teams to event
121
+ ## for now check if team is alreay included
122
+ ## todo/fix: clear/destroy_all first - why? why not!!!
123
+ team_recs << team_rec unless team_ids.include?( team_rec.id )
124
+ end
103
125
 
104
- pp round_recs
105
126
 
106
- round_recs.each do |round_rec|
127
+ rounds.each do |round|
107
128
  ## quick hack: if pos missing fill with dummy 999 for now
108
- round_rec.pos = 999 if round_rec.pos.nil?
109
- round = Sync::Round.find_or_create( round_rec, event: event ) ## check: use/rename to EventRound why? why not?
129
+ round.pos = 999 if round.pos.nil?
130
+ round_rec = Sync::Round.find_or_create( round, event: event_rec ) ## check: use/rename to EventRound why? why not?
110
131
  end
111
132
 
112
- match_recs.each do |match_rec|
133
+ groups.each do |group|
134
+ group_rec = Sync::Group.find_or_create( group, event: event_rec ) ## check: use/rename to EventGroup why? why not?
135
+ end
136
+
137
+ matches.each do |match|
113
138
  ## todo/fix: pass along stage (if present): stage - optional!!!!
114
- match = Sync::Match.create_or_update( match_rec, event: event )
139
+ match_rec = Sync::Match.create_or_update( match, event: event_rec )
115
140
  end
116
141
  end
117
142
 
118
- recs
119
- end # method read
143
+ true ## success/ok
144
+ end # method parse
145
+
146
+
147
+ ######################
148
+ # (convenience) helpers
149
+
150
+ def catalog() Import.catalog; end
151
+
120
152
  end # class MatchReaderV2
121
153
  end # module SportDb