sportdb-readers 0.5.0 → 1.0.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.
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