sportdb-config 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/HISTORY.md +3 -0
- data/Manifest.txt +65 -0
- data/README.md +27 -0
- data/Rakefile +26 -0
- data/config/geos/eng.txt +162 -0
- data/config/leagues/eng.txt +40 -0
- data/config/leagues/fr.txt +9 -0
- data/config/leagues/gr.txt +7 -0
- data/config/leagues/sco.txt +19 -0
- data/config/teams/al.txt +6 -0
- data/config/teams/at.txt +26 -0
- data/config/teams/be.txt +58 -0
- data/config/teams/bg.txt +6 -0
- data/config/teams/by.txt +5 -0
- data/config/teams/ca.txt +4 -0
- data/config/teams/ch.txt +10 -0
- data/config/teams/cy.txt +7 -0
- data/config/teams/cz.txt +6 -0
- data/config/teams/de.txt +129 -0
- data/config/teams/dk.txt +6 -0
- data/config/teams/eng.txt +294 -0
- data/config/teams/es.txt +148 -0
- data/config/teams/fi.txt +6 -0
- data/config/teams/fr.txt +98 -0
- data/config/teams/ga.txt +5 -0
- data/config/teams/gr.txt +87 -0
- data/config/teams/hr.txt +9 -0
- data/config/teams/hu.txt +5 -0
- data/config/teams/ie.txt +5 -0
- data/config/teams/is.txt +6 -0
- data/config/teams/it.txt +99 -0
- data/config/teams/lu.txt +6 -0
- data/config/teams/mc.txt +4 -0
- data/config/teams/md.txt +4 -0
- data/config/teams/mt.txt +6 -0
- data/config/teams/mx.txt +30 -0
- data/config/teams/nir.txt +6 -0
- data/config/teams/nl.txt +45 -0
- data/config/teams/no.txt +7 -0
- data/config/teams/pl.txt +6 -0
- data/config/teams/pt.txt +53 -0
- data/config/teams/ro.txt +5 -0
- data/config/teams/rs.txt +6 -0
- data/config/teams/ru.txt +5 -0
- data/config/teams/sco.txt +73 -0
- data/config/teams/se.txt +6 -0
- data/config/teams/si.txt +5 -0
- data/config/teams/sk.txt +5 -0
- data/config/teams/tr.txt +39 -0
- data/config/teams/ua.txt +5 -0
- data/config/teams/us.txt +44 -0
- data/config/teams/wal.txt +16 -0
- data/lib/sportdb/config.rb +26 -0
- data/lib/sportdb/config/config.rb +77 -0
- data/lib/sportdb/config/league.rb +118 -0
- data/lib/sportdb/config/league_reader.rb +65 -0
- data/lib/sportdb/config/structs/team.rb +45 -0
- data/lib/sportdb/config/team_reader.rb +108 -0
- data/lib/sportdb/config/utils/league_utils.rb +24 -0
- data/lib/sportdb/config/utils/season_utils.rb +116 -0
- data/lib/sportdb/config/version.rb +35 -0
- data/test/helper.rb +10 -0
- data/test/test_config.rb +19 -0
- data/test/test_league_utils.rb +90 -0
- data/test/test_season_utils.rb +29 -0
- metadata +189 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
module Import
|
5
|
+
|
6
|
+
|
7
|
+
class LeagueConfig ## use LeagueInfo or LeagueMap or LeagueHash or similar
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
|
11
|
+
## just use leagues without latest for latest - why? why not?
|
12
|
+
@leagues_latest = {
|
13
|
+
'es' => { '1' => 'liga', # spanish liga 1
|
14
|
+
'2' => 'liga2', # spanish liga 2
|
15
|
+
},
|
16
|
+
'it' => { '1' => 'seriea', # italian serie a
|
17
|
+
'2' => 'serieb', # italian serie b
|
18
|
+
},
|
19
|
+
'de' => { '1' => 'bundesliga', # german bundesliga
|
20
|
+
'2' => 'bundesliga2', # german 2. bundesliga
|
21
|
+
},
|
22
|
+
'nl' => { '1' => 'eredivisie' }, # dutch eredivisie
|
23
|
+
'be' => { '1' => 'proleague' }, # belgian pro league
|
24
|
+
'pt' => { '1' => 'liga' }, # portugese Primeira Liga
|
25
|
+
'tr' => { '1' => 'superlig' }, # turkish Süper Lig
|
26
|
+
|
27
|
+
# note: eng now read from txt
|
28
|
+
# 'eng' => { '1' => 'premierleague', # english premier league
|
29
|
+
# '2' => 'championship', # english championship league
|
30
|
+
# '3' => 'league1', # english league 1
|
31
|
+
# },
|
32
|
+
}
|
33
|
+
|
34
|
+
## change history to past or changes/changelog something - why? why not?
|
35
|
+
@leagues_history = {
|
36
|
+
|
37
|
+
# note: eng now read from txt
|
38
|
+
# 'eng' => {
|
39
|
+
# ## until (including) 2003-04 season
|
40
|
+
# '2003-04' => { '1' => 'premierleague', # english premier league
|
41
|
+
# '2' => 'division1', # english division 1
|
42
|
+
# },
|
43
|
+
# ## until (including) 1991-92} season
|
44
|
+
# '1991-92' => { '1' => 'division1', # english division 1
|
45
|
+
# '2' => 'division2', # english division 2
|
46
|
+
# }
|
47
|
+
# }
|
48
|
+
}
|
49
|
+
|
50
|
+
pp @leagues_latest
|
51
|
+
pp @leagues_history
|
52
|
+
|
53
|
+
%w(eng sco fr gr).each do |country|
|
54
|
+
hash = LeagueReader.from_file( "#{Boot.data_dir}/leagues/#{country}.txt" )
|
55
|
+
pp hash
|
56
|
+
|
57
|
+
hash.each do |season,league_hash|
|
58
|
+
if season == '*' ## assume latest / default season
|
59
|
+
@leagues_latest[ country ] = league_hash
|
60
|
+
else
|
61
|
+
@leagues_history[ country ] ||= {}
|
62
|
+
@leagues_history[ country ][ season ] = league_hash
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
pp @leagues_latest
|
68
|
+
pp @leagues_history
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
def basename( league, country:, season: )
|
74
|
+
## todo/check: rename league: to key: - why? why not?
|
75
|
+
|
76
|
+
if country.include?( '-' ) ## assume package name e.g. eng-england etc.
|
77
|
+
## cut off country code from package name
|
78
|
+
cc = country.split( '-' )[0] # use first part
|
79
|
+
else
|
80
|
+
cc = country
|
81
|
+
end
|
82
|
+
|
83
|
+
if season
|
84
|
+
puts " checking season >#{season}<"
|
85
|
+
## check history if season is provided / supplied / known
|
86
|
+
history = @leagues_history[ cc ]
|
87
|
+
if history
|
88
|
+
season_start_year = SeasonUtils.start_year( season ).to_i
|
89
|
+
##
|
90
|
+
## todo: sorty season keys - why? why not? -- assume reverse chronological order for now
|
91
|
+
history.keys.reverse.each do |key|
|
92
|
+
history_season_start_year = SeasonUtils.start_year( key ).to_i
|
93
|
+
puts " #{season_start_year} <= #{history_season_start_year} - #{season_start_year <= history_season_start_year}"
|
94
|
+
if season_start_year <= history_season_start_year
|
95
|
+
result = history[ key ][ league ]
|
96
|
+
if result
|
97
|
+
return "#{league}-#{result}"
|
98
|
+
else
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
latest = @leagues_latest[ cc ]
|
107
|
+
if latest
|
108
|
+
result = latest[ league ]
|
109
|
+
return "#{league}-#{result}" if result
|
110
|
+
end
|
111
|
+
|
112
|
+
nil
|
113
|
+
end # method basename
|
114
|
+
end # class LeagueConfig
|
115
|
+
|
116
|
+
|
117
|
+
end ## module Import
|
118
|
+
end ## module SportDb
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module SportDb
|
5
|
+
module Import
|
6
|
+
|
7
|
+
class LeagueReader
|
8
|
+
|
9
|
+
def self.from_file( path ) ## use - rename to read_file - why? why not?
|
10
|
+
txt = File.open( path, 'r:utf-8' ).read
|
11
|
+
read( txt )
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
SEASON_REGEX = /\[
|
17
|
+
(?<season>
|
18
|
+
\d{4}
|
19
|
+
(-\d{2,4})?
|
20
|
+
)
|
21
|
+
\]/x
|
22
|
+
|
23
|
+
def self.read( txt )
|
24
|
+
hash = {}
|
25
|
+
season = '*' ## use '*' for default/latest season
|
26
|
+
|
27
|
+
txt.each_line do |line|
|
28
|
+
line = line.strip
|
29
|
+
|
30
|
+
next if line.empty?
|
31
|
+
next if line.start_with?( '#' ) ## skip comments too
|
32
|
+
|
33
|
+
## strip inline comments too
|
34
|
+
line = line.sub( /#.*/, '' ).strip
|
35
|
+
|
36
|
+
pp line
|
37
|
+
|
38
|
+
|
39
|
+
if (m=line.match( SEASON_REGEX ))
|
40
|
+
season = m[:season]
|
41
|
+
else
|
42
|
+
key_line, values_line = line.split( '=>' )
|
43
|
+
values = values_line.split( ',' )
|
44
|
+
|
45
|
+
## remove leading and trailing spaces
|
46
|
+
key_line = key_line.strip
|
47
|
+
values = values.map { |value| value.strip }
|
48
|
+
pp values
|
49
|
+
|
50
|
+
league_key = key_line
|
51
|
+
league_basename = values[0]
|
52
|
+
|
53
|
+
hash[season] ||= {}
|
54
|
+
hash[season][league_key] = league_basename
|
55
|
+
end
|
56
|
+
end
|
57
|
+
hash
|
58
|
+
end # method read
|
59
|
+
|
60
|
+
end ## class LeagueReader
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
end ## module Import
|
65
|
+
end ## module SportDb
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
module Struct
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
class Team
|
9
|
+
attr_accessor :name,
|
10
|
+
:alt_names,
|
11
|
+
:city,
|
12
|
+
:country
|
13
|
+
|
14
|
+
|
15
|
+
## add alias/compat - why? why not
|
16
|
+
def title() @name; end
|
17
|
+
def names()
|
18
|
+
ary = [@name]
|
19
|
+
ary += @alt_names if @alt_names
|
20
|
+
ary
|
21
|
+
end ## all names
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
## do nothing for now (use from_csv to setup data)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create( **kwargs )
|
30
|
+
self.new.update( kwargs )
|
31
|
+
end
|
32
|
+
|
33
|
+
def update( **kwargs )
|
34
|
+
@name = kwargs[:name]
|
35
|
+
@alt_names = kwargs[:alt_names]
|
36
|
+
@city = kwargs[:city] ## use city struct - why? why not?
|
37
|
+
## todo: add country too
|
38
|
+
|
39
|
+
self ## note - MUST return self for chaining
|
40
|
+
end
|
41
|
+
end # class Team
|
42
|
+
|
43
|
+
|
44
|
+
end # module Struct
|
45
|
+
end # module SportDb
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module SportDb
|
5
|
+
module Import
|
6
|
+
|
7
|
+
|
8
|
+
class TeamReader
|
9
|
+
|
10
|
+
|
11
|
+
def self.from_file( path ) ## use - rename to read_file - why? why not?
|
12
|
+
txt = File.open( path, 'r:utf-8' ).read
|
13
|
+
read( txt )
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def self.read( txt ) ## rename to parse - why? why not? and use read for file read?
|
18
|
+
recs = []
|
19
|
+
txt.each_line do |line|
|
20
|
+
line = line.strip
|
21
|
+
|
22
|
+
next if line.empty?
|
23
|
+
next if line.start_with?( '#' ) ## skip comments too
|
24
|
+
|
25
|
+
## strip inline comments too
|
26
|
+
## e.g Eupen => KAS Eupen, ## [de]
|
27
|
+
## => Eupen => KAS Eupen,
|
28
|
+
line = line.sub( /#.*/, '' ).strip
|
29
|
+
|
30
|
+
pp line
|
31
|
+
names_line, team_line = line.split( '=>' )
|
32
|
+
|
33
|
+
names = names_line.split( /[|,]/ ) # team names - allow comma(,) or pipe(|)
|
34
|
+
team = team_line.split( ',' ) # (canoncial) team name, team_city
|
35
|
+
|
36
|
+
## remove leading and trailing spaces
|
37
|
+
names = names.map { |name| name.strip }
|
38
|
+
team = team.map { |team| team.strip }
|
39
|
+
pp names
|
40
|
+
pp team
|
41
|
+
|
42
|
+
canonical_name = team[0]
|
43
|
+
city = team[1]
|
44
|
+
|
45
|
+
## squish (white)spaces
|
46
|
+
# e.g. New York FC (2011-) => New York FC (2011-)
|
47
|
+
# e.g. León › Guanajuato => León › Guanajuato
|
48
|
+
canonical_name = canonical_name.gsub( /[ \t]+/, ' ' )
|
49
|
+
city = city.gsub( /[ \t]+/, ' ' ) if city
|
50
|
+
|
51
|
+
## note:
|
52
|
+
## check/todo!!!!!!!!!!!!!!!!!-
|
53
|
+
## strip year if to present e.g. (2011-)
|
54
|
+
##
|
55
|
+
## do NOT strip for defunct / historic clubs e.g.
|
56
|
+
## (1899-1910)
|
57
|
+
## or (-1914) or (-2011) etc.
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
###
|
62
|
+
## todo: move year out of canonical team name - why? why not?
|
63
|
+
##
|
64
|
+
|
65
|
+
## check if canonical name include (2011-) or similar in name
|
66
|
+
## if yes, remove (2011-) and add to (alt) names
|
67
|
+
## e.g. New York FC (2011) => New York FC
|
68
|
+
if canonical_name =~ /\(.+?\)/ ## note: use non-greedy (?) match
|
69
|
+
name = canonical_name.gsub( /\(.+?\)/, '' ).strip
|
70
|
+
names << name
|
71
|
+
end
|
72
|
+
|
73
|
+
### todo/fix:
|
74
|
+
## auto-add alt name with dots stripped - why? why not?
|
75
|
+
## e.g. D.C. United => DC United
|
76
|
+
## e.g. Liverpool F.C. => Liverpool FC
|
77
|
+
## e.g. St. Albin => St Albin etc.
|
78
|
+
## e.g. 1. FC Köln => 1 FC Köln -- make special case for 1. - why? why not?
|
79
|
+
|
80
|
+
##
|
81
|
+
## todo/fix: unify mapping entries
|
82
|
+
## always lowercase !!!! (case insensitive)
|
83
|
+
## always strip (2011-) !!!
|
84
|
+
## always strip dots (e.g. St., F.C, etc.)
|
85
|
+
|
86
|
+
|
87
|
+
names = names.uniq ## remove duplicates - todo/fix: warn about duplicates - why? why not?
|
88
|
+
|
89
|
+
## note: remove from alt_names if canonical name (mapping 1:1)
|
90
|
+
## or if empty
|
91
|
+
alt_names = names.select {|name| (name.empty? || name == canonical_name) ? false : true }
|
92
|
+
|
93
|
+
## todo: add country (code) too!!!
|
94
|
+
rec = SportDb::Struct::Team.create(
|
95
|
+
name: canonical_name,
|
96
|
+
city: city, ## note: team_city is optional for now (might be nil!!!)
|
97
|
+
alt_names: alt_names
|
98
|
+
)
|
99
|
+
## pp rec
|
100
|
+
recs << rec
|
101
|
+
end
|
102
|
+
recs
|
103
|
+
end # method read
|
104
|
+
end # class TeamReader
|
105
|
+
|
106
|
+
|
107
|
+
end ## module Import
|
108
|
+
end ## module SportDb
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module LeagueHelper
|
5
|
+
def basename( league, country:, season: nil )
|
6
|
+
## e.g. eng-england, 2011-12, 1 returns 1-premierleague
|
7
|
+
##
|
8
|
+
## allow country code or (repo) package name
|
9
|
+
## e.g. eng-england or eng
|
10
|
+
## de-deutschland or de etc.
|
11
|
+
|
12
|
+
leagues = SportDb::Import.config.leagues
|
13
|
+
|
14
|
+
result = leagues.basename( league, country: country, season: season )
|
15
|
+
|
16
|
+
##
|
17
|
+
# note: if no mapping / nothing found return league e.g. 1, 2, 3, 3a, 3b, cup(?), etc.
|
18
|
+
result ? result : league
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module LeagueUtils
|
23
|
+
extend LeagueHelper
|
24
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module SeasonHelper ## use Helpers why? why not?
|
5
|
+
def prev( season )
|
6
|
+
## todo: add 1964-1965 format too!!!
|
7
|
+
if season =~ /^(\d{4})-(\d{2})$/ ## season format is 1964-65
|
8
|
+
fst = $1.to_i - 1
|
9
|
+
snd = (100 + $2.to_i - 1) % 100 ## note: add 100 to turn 00 => 99
|
10
|
+
"%4d-%02d" % [fst,snd]
|
11
|
+
elsif season =~ /^(\d{4})$/
|
12
|
+
fst = $1.to_i - 1
|
13
|
+
"%4d" % [fst]
|
14
|
+
else
|
15
|
+
puts "*** !!!! wrong season format >>#{season}<<; exit; sorry"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
end # method prev
|
19
|
+
|
20
|
+
|
21
|
+
def key( basename )
|
22
|
+
if basename =~ /^(\d{4})[\-\/](\d{4})$/ ## e.g. 2011-2012 or 2011/2012 => 2011/12
|
23
|
+
"%4d/%02d" % [$1.to_i, $2.to_i % 100]
|
24
|
+
elsif basename =~ /^(\d{4})[\-\/](\d{2})$/ ## e.g. 2011-12 or 2011/12 => 2011/12
|
25
|
+
"#{$1}/#{$2}"
|
26
|
+
elsif basename =~ /^(\d{4})$/
|
27
|
+
$1
|
28
|
+
else
|
29
|
+
puts "*** !!!! wrong season format >>#{basename}<<; exit; sorry"
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end # method key
|
33
|
+
|
34
|
+
|
35
|
+
def directory( season, format: nil )
|
36
|
+
## todo: find better names for formats - why? why not?:
|
37
|
+
## long | archive | decade(?) => 1980s/1988-89, 2010s/2017-18, ...
|
38
|
+
## short | std(?) => 1988-89, 2017-18, ...
|
39
|
+
|
40
|
+
## convert season name to "standard" season name for directory
|
41
|
+
|
42
|
+
## todo/fix: move to parse / validate season (for (re)use)!!!! - why? why not?
|
43
|
+
if season =~ /^(\d{4})[\-\/](\d{4})$/ ## e.g. 2011-2012 or 2011/2012 => 2011-12
|
44
|
+
years = [$1.to_i, $2.to_i]
|
45
|
+
elsif season =~ /^(\d{4})[\-\/](\d{2})$/ ## e.g. 2011-12 or 2011/12 => 2011-12
|
46
|
+
years = [$1.to_i, $1.to_i+1]
|
47
|
+
## note: check that season end year is (always) season start year + 1
|
48
|
+
if ($1.to_i+1) % 100 != $2.to_i
|
49
|
+
puts "*** !!!! wrong season format >>#{season}<<; season end year MUST (always) equal season start year + 1; exit; sorry"
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
elsif season =~ /^(\d{4})$/
|
53
|
+
years = [$1.to_i]
|
54
|
+
else
|
55
|
+
puts "*** !!!! wrong season format >>#{season}<<; exit; sorry"
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
if ['l', 'long', 'archive' ].include?( format.to_s ) ## note: allow passing in of symbol to e.g. 'long' or :long
|
61
|
+
if years.size == 2
|
62
|
+
"%3d0s/%4d-%02d" % [years[0] / 10, years[0], years[1] % 100] ## e.g. 2000s/2001-02
|
63
|
+
else ## assume size 1 (single year season)
|
64
|
+
"%3d0s/%4d" % [years[0] / 10, years[0]]
|
65
|
+
end
|
66
|
+
else ## default 'short' format / fallback
|
67
|
+
if years.size == 2
|
68
|
+
"%4d-%02d" % [years[0], years[1] % 100] ## e.g. 2001-02
|
69
|
+
else ## assume size 1 (single year season)
|
70
|
+
"%4d" % years[0]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end # method directory
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
def start_year( season ) ## get start year
|
78
|
+
## convert season name to "standard" season name for directory
|
79
|
+
|
80
|
+
## todo/check: just return year from first for chars - keep it simple - why? why not?
|
81
|
+
if season =~ /^(\d{4})[\-\/](\d{4})$/ ## e.g. 2011-2010 or 2011/2011 => 2011-10
|
82
|
+
$1
|
83
|
+
elsif season =~ /^(\d{4})[\-\/](\d{2})$/
|
84
|
+
$1
|
85
|
+
elsif season =~ /^(\d{4})$/
|
86
|
+
$1
|
87
|
+
else
|
88
|
+
puts "*** !!!! wrong season format >>#{season}<<; exit; sorry"
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def end_year( season ) ## get end year
|
94
|
+
## convert season name to "standard" season name for directory
|
95
|
+
if season =~ /^(\d{4})[\-\/](\d{4})$/ ## e.g. 2011-2010 or 2011/2011 => 2011-10
|
96
|
+
$2
|
97
|
+
elsif season =~ /^(\d{4})[\-\/](\d{2})$/
|
98
|
+
## note: assume second year is always +1
|
99
|
+
## todo/fix: add assert/check - why? why not?
|
100
|
+
## eg. 1999-00 => 2000 or 1899-00 => 1900
|
101
|
+
($1.to_i+1).to_s
|
102
|
+
elsif season =~ /^(\d{4})$/
|
103
|
+
$1
|
104
|
+
else
|
105
|
+
puts "*** !!!! wrong season format >>#{season}<<; exit; sorry"
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end # module SeasonHelper
|
110
|
+
|
111
|
+
|
112
|
+
module SeasonUtils
|
113
|
+
extend SeasonHelper
|
114
|
+
## lets you use SeasonHelper as "globals" eg.
|
115
|
+
## SeasonUtils.prev( season ) etc.
|
116
|
+
end # SeasonUtils
|