sports 0.1.0 → 0.2.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 +5 -5
- data/CHANGELOG.md +1 -0
- data/Manifest.txt +0 -23
- data/README.md +3 -7
- data/Rakefile +4 -7
- data/lib/sports/version.rb +2 -2
- data/lib/sports.rb +5 -72
- metadata +20 -80
- data/lib/sports/config.rb +0 -40
- data/lib/sports/goal_parser_csv.rb +0 -28
- data/lib/sports/match_parser_csv.rb +0 -490
- data/lib/sports/match_status_parser.rb +0 -90
- data/lib/sports/name_helper.rb +0 -87
- data/lib/sports/season.rb +0 -199
- data/lib/sports/structs/country.rb +0 -26
- data/lib/sports/structs/goal.rb +0 -231
- data/lib/sports/structs/group.rb +0 -16
- data/lib/sports/structs/league.rb +0 -35
- data/lib/sports/structs/match.rb +0 -180
- data/lib/sports/structs/matchlist.rb +0 -215
- data/lib/sports/structs/round.rb +0 -23
- data/lib/sports/structs/standings.rb +0 -271
- data/lib/sports/structs/team.rb +0 -147
- data/lib/sports/structs/team_usage.rb +0 -84
- data/test/helper.rb +0 -12
- data/test/test_clubs.rb +0 -38
- data/test/test_csv_reader.rb +0 -30
- data/test/test_match.rb +0 -30
- data/test/test_match_status_parser.rb +0 -57
- data/test/test_name_helper.rb +0 -65
- data/test/test_season.rb +0 -141
@@ -1,215 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Sports
|
4
|
-
|
5
|
-
|
6
|
-
class Matchlist ## todo: find a better name - MatchStats, MatchFixtures, MatchSchedule, ...
|
7
|
-
## use MatchCache/Buffer/Summary/Snippet/Segment/List...
|
8
|
-
## or MatchAnalyzer/Checker/Proofer/Query - why? why not?
|
9
|
-
attr_reader :matches # count of matches
|
10
|
-
## :name,
|
11
|
-
## :goals, # count of (total) goals - use total_goals - why? why not?
|
12
|
-
## :teams, -- has its own reader
|
13
|
-
## :rounds # note: use if all teams have same match count
|
14
|
-
## add last_updated/updated or something - why? why not?
|
15
|
-
|
16
|
-
def initialize( matches )
|
17
|
-
@matches = matches
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
def usage
|
22
|
-
@usage ||= build_usage( @matches )
|
23
|
-
@usage
|
24
|
-
end
|
25
|
-
|
26
|
-
def team_usage() usage.team_usage; end
|
27
|
-
|
28
|
-
def teams
|
29
|
-
@team_names ||= team_usage.keys.sort
|
30
|
-
@team_names
|
31
|
-
end
|
32
|
-
|
33
|
-
def goals() usage.goals; end
|
34
|
-
|
35
|
-
## note: start_date and end_date might be nil / optional missing!!!!
|
36
|
-
def start_date?() usage.start_date?; end
|
37
|
-
def end_date?() usage.end_date?; end
|
38
|
-
|
39
|
-
def start_date() usage.start_date; end
|
40
|
-
def end_date() usage.end_date; end
|
41
|
-
|
42
|
-
def has_dates?() usage.has_dates?; end
|
43
|
-
def dates_str() usage.dates_str; end
|
44
|
-
def days() usage.days; end
|
45
|
-
|
46
|
-
|
47
|
-
def rounds() usage.rounds; end
|
48
|
-
|
49
|
-
## todo: add has_rounds? alias for rounds? too
|
50
|
-
## return true if all match_played in team_usage are the same
|
51
|
-
## e.g. assumes league with matchday rounds
|
52
|
-
def rounds?() usage.rounds?; end
|
53
|
-
|
54
|
-
def match_counts() usage.match_counts; end
|
55
|
-
def match_counts_str() usage.match_counts_str; end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def stage_usage
|
60
|
-
@stage_usage ||= build_stage_usage( @matches )
|
61
|
-
@stage_usage
|
62
|
-
end
|
63
|
-
|
64
|
-
def stages() stage_usage.keys; end ## note: returns empty array for stages for now - why? why not?
|
65
|
-
|
66
|
-
|
67
|
-
############################
|
68
|
-
# matchlist helpers
|
69
|
-
private
|
70
|
-
class StatLine
|
71
|
-
attr_reader :team_usage,
|
72
|
-
:matches,
|
73
|
-
:goals,
|
74
|
-
:rounds, ## keep rounds - why? why not?
|
75
|
-
:start_date,
|
76
|
-
:end_date
|
77
|
-
|
78
|
-
def teams() @team_usage.keys.sort; end ## (auto-)sort here always - why? why not?
|
79
|
-
|
80
|
-
def start_date?() @start_date.nil? == false; end
|
81
|
-
def end_date?() @end_date.nil? == false; end
|
82
|
-
|
83
|
-
def has_dates?() @start_date && @end_date; end
|
84
|
-
def dates_str
|
85
|
-
## note: start_date/end_date might be optional/missing
|
86
|
-
if has_dates?
|
87
|
-
"#{start_date.strftime( '%a %d %b %Y' )} - #{end_date.strftime( '%a %d %b %Y' )}"
|
88
|
-
else
|
89
|
-
"??? - ???"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def days() end_date.jd - start_date.jd; end
|
94
|
-
|
95
|
-
|
96
|
-
def rounds
|
97
|
-
rounds? ## note: use rounds? to calculate (cache) rounds
|
98
|
-
@rounds ## note: return number of rounds or nil (for uneven matches played by teams)
|
99
|
-
end
|
100
|
-
|
101
|
-
## todo: add has_rounds? alias for rounds? too
|
102
|
-
def rounds?
|
103
|
-
## return true if all match_played in team_usage are the same
|
104
|
-
## e.g. assumes league with matchday rounds
|
105
|
-
if @has_rounds.nil? ## check/todo: if undefined attribute is nil by default??
|
106
|
-
## check/calc rounds
|
107
|
-
## note: values => matches_played by team
|
108
|
-
if match_counts.size == 1
|
109
|
-
@rounds = match_counts[0][0]
|
110
|
-
else
|
111
|
-
@rounds = nil
|
112
|
-
end
|
113
|
-
@has_rounds = @rounds ? true : false
|
114
|
-
end
|
115
|
-
@has_rounds
|
116
|
-
end
|
117
|
-
|
118
|
-
|
119
|
-
def build_match_counts ## use/rename to matches_played - why? why not?
|
120
|
-
counts = Hash.new(0)
|
121
|
-
team_usage.values.each do |count|
|
122
|
-
counts[count] += 1
|
123
|
-
end
|
124
|
-
|
125
|
-
## sort (descending) highest usage value first (in returned array)
|
126
|
-
## e.g. [[32,8],[31,2]] ## 32 matches by 8 teams, 31 matches by 2 teams etc.
|
127
|
-
counts.sort_by {|count, usage| -count }
|
128
|
-
end
|
129
|
-
|
130
|
-
def match_counts
|
131
|
-
# match counts / nos played per team
|
132
|
-
@match_counts ||= build_match_counts
|
133
|
-
@match_counts
|
134
|
-
end
|
135
|
-
|
136
|
-
def match_counts_str
|
137
|
-
## pretty print / formatted match_counts
|
138
|
-
buf = String.new('')
|
139
|
-
match_counts.each_with_index do |rec,i|
|
140
|
-
buf << ' ' if i > 0 ## add (space) separator
|
141
|
-
buf << "#{rec[0]}×#{rec[1]}"
|
142
|
-
end
|
143
|
-
buf
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
def initialize
|
149
|
-
@matches = 0
|
150
|
-
@goals = 0
|
151
|
-
|
152
|
-
@start_date = nil
|
153
|
-
@end_date = nil
|
154
|
-
|
155
|
-
@team_usage = Hash.new(0)
|
156
|
-
|
157
|
-
@match_counts = nil
|
158
|
-
end
|
159
|
-
|
160
|
-
|
161
|
-
def update( match )
|
162
|
-
@matches += 1 ## match counter
|
163
|
-
|
164
|
-
if match.score1 && match.score2
|
165
|
-
@goals += match.score1
|
166
|
-
@goals += match.score2
|
167
|
-
|
168
|
-
## todo: add after extra time? if knock out (k.o.) - why? why not?
|
169
|
-
## make it a flag/opt?
|
170
|
-
end
|
171
|
-
|
172
|
-
@team_usage[ match.team1 ] += 1
|
173
|
-
@team_usage[ match.team2 ] += 1
|
174
|
-
|
175
|
-
if match.date
|
176
|
-
## return / store date as string as is - why? why not?
|
177
|
-
date = Date.strptime( match.date, '%Y-%m-%d' )
|
178
|
-
|
179
|
-
@start_date = date if @start_date.nil? || date < @start_date
|
180
|
-
@end_date = date if @end_date.nil? || date > @end_date
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end # class StatLine
|
184
|
-
|
185
|
-
|
186
|
-
## collect total usage stats (for all matches)
|
187
|
-
def build_usage( matches )
|
188
|
-
stat = StatLine.new
|
189
|
-
matches.each do |match|
|
190
|
-
stat.update( match )
|
191
|
-
end
|
192
|
-
stat
|
193
|
-
end
|
194
|
-
|
195
|
-
## collect usage stats by stage (e.g. regular / playoff / etc.)
|
196
|
-
def build_stage_usage( matches )
|
197
|
-
stages = {}
|
198
|
-
|
199
|
-
matches.each do |match|
|
200
|
-
stage_key = if match.stage.nil?
|
201
|
-
'Regular' ## note: assume Regular stage if not defined (AND not explicit unknown)
|
202
|
-
else
|
203
|
-
match.stage
|
204
|
-
end
|
205
|
-
|
206
|
-
stages[ stage_key ] ||= StatLine.new
|
207
|
-
stages[ stage_key ].update( match )
|
208
|
-
end
|
209
|
-
|
210
|
-
stages
|
211
|
-
end
|
212
|
-
|
213
|
-
end # class Matchlist
|
214
|
-
|
215
|
-
end # module Sports
|
data/lib/sports/structs/round.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Sports
|
2
|
-
|
3
|
-
class Round
|
4
|
-
attr_reader :name, :start_date, :end_date, :knockout
|
5
|
-
attr_accessor :num # note: make read & writable - why? why not?
|
6
|
-
|
7
|
-
def initialize( name:,
|
8
|
-
num: nil,
|
9
|
-
start_date: nil,
|
10
|
-
end_date: nil,
|
11
|
-
knockout: false,
|
12
|
-
auto: true )
|
13
|
-
@name = name
|
14
|
-
@num = num
|
15
|
-
@start_date = start_date
|
16
|
-
@end_date = end_date
|
17
|
-
@knockout = knockout
|
18
|
-
@auto = auto # auto-created (inline reference/header without proper definition before)
|
19
|
-
end
|
20
|
-
end # class Round
|
21
|
-
|
22
|
-
end # module Sports
|
23
|
-
|
@@ -1,271 +0,0 @@
|
|
1
|
-
##########
|
2
|
-
# todo/fix:
|
3
|
-
## reuse standings helper/calculator from sportdb
|
4
|
-
## do NOT duplicate
|
5
|
-
|
6
|
-
|
7
|
-
module Sports
|
8
|
-
|
9
|
-
|
10
|
-
class Standings
|
11
|
-
|
12
|
-
class StandingsLine ## nested class StandinsLine - todo/fix: change to Line - why? why not?
|
13
|
-
attr_accessor :rank, :team,
|
14
|
-
:played, :won, :lost, :drawn, ## -- total
|
15
|
-
:goals_for, :goals_against, :pts,
|
16
|
-
:home_played, :home_won, :home_lost, :home_drawn, ## -- home
|
17
|
-
:home_goals_for, :home_goals_against, :home_pts,
|
18
|
-
:away_played, :away_won, :away_lost, :away_drawn, ## -- away
|
19
|
-
:away_goals_for, :away_goals_against, :away_pts
|
20
|
-
|
21
|
-
alias_method :team_name, :team ## note: team for now always a string
|
22
|
-
alias_method :pos, :rank ## rename back to use pos instead of rank - why? why not?
|
23
|
-
|
24
|
-
|
25
|
-
def initialize( team )
|
26
|
-
@rank = nil # use 0? why? why not?
|
27
|
-
## change rank back to pos - why? why not?
|
28
|
-
@team = team
|
29
|
-
@played = @home_played = @away_played = 0
|
30
|
-
@won = @home_won = @away_won = 0
|
31
|
-
@lost = @home_lost = @away_lost = 0
|
32
|
-
@drawn = @home_drawn = @away_drawn = 0
|
33
|
-
@goals_for = @home_goals_for = @away_goals_for = 0
|
34
|
-
@goals_against = @home_goals_against = @away_goals_against = 0
|
35
|
-
@pts = @home_pts = @away_pts = 0
|
36
|
-
end
|
37
|
-
end # (nested) class StandingsLine
|
38
|
-
|
39
|
-
|
40
|
-
def initialize( match_or_matches=nil, opts={} )
|
41
|
-
## fix:
|
42
|
-
# passing in e.g. pts for win (3? 2? etc.)
|
43
|
-
# default to 3 for now
|
44
|
-
|
45
|
-
## lets you pass in 2 as an alterantive, for example
|
46
|
-
@pts_won = opts[:pts_won] || 3
|
47
|
-
|
48
|
-
@lines = {} # StandingsLines cached by team name/key
|
49
|
-
|
50
|
-
## add init and update all-in-one convenience shortcut
|
51
|
-
update( match_or_matches ) if match_or_matches
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
def update( match_or_matches )
|
56
|
-
## convenience - update all matches at once
|
57
|
-
## todo/check: check for ActiveRecord_Associations_CollectionProxy and than use to_a (to "force" array) - why? why not?
|
58
|
-
matches = if match_or_matches.is_a?(Array)
|
59
|
-
match_or_matches
|
60
|
-
else
|
61
|
-
[match_or_matches]
|
62
|
-
end
|
63
|
-
|
64
|
-
matches.each_with_index do |match,i| # note: index(i) starts w/ zero (0)
|
65
|
-
update_match( match )
|
66
|
-
end
|
67
|
-
self # note: return self to allow chaining
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
## note: add a convenience shortcut
|
72
|
-
## to_a will sort and add rank (1,2,3) to standing lines
|
73
|
-
def each( &block ) to_a.each( &block ); end
|
74
|
-
|
75
|
-
def to_a
|
76
|
-
## return lines; sort and add rank
|
77
|
-
## note: will update rank!!!! (side effect)
|
78
|
-
|
79
|
-
#############################
|
80
|
-
### calc ranking position (rank)
|
81
|
-
## fix/allow same rank e.g. all 1 or more than one team 3rd etc.
|
82
|
-
|
83
|
-
# build array from hash
|
84
|
-
ary = []
|
85
|
-
@lines.each do |k,v|
|
86
|
-
ary << v
|
87
|
-
end
|
88
|
-
|
89
|
-
ary.sort! do |l,r|
|
90
|
-
## note: reverse order (thus, change l,r to r,l)
|
91
|
-
value = r.pts <=> l.pts
|
92
|
-
if value == 0 # same pts try goal diff
|
93
|
-
value = (r.goals_for-r.goals_against) <=> (l.goals_for-l.goals_against)
|
94
|
-
if value == 0 # same goal diff too; try assume more goals better for now
|
95
|
-
value = r.goals_for <=> l.goals_for
|
96
|
-
end
|
97
|
-
end
|
98
|
-
value
|
99
|
-
end
|
100
|
-
|
101
|
-
## update rank using ordered array
|
102
|
-
ary.each_with_index do |line,i|
|
103
|
-
line.rank = i+1 ## add ranking (e.g. 1,2,3 etc.) - note: i starts w/ zero (0)
|
104
|
-
end
|
105
|
-
|
106
|
-
ary
|
107
|
-
end # to_a
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
#####
|
112
|
-
###
|
113
|
-
## fix: move build to StandingsPart/Report !!!!
|
114
|
-
def build( source: nil ) ## build / pretty print standings table in string buffer
|
115
|
-
## keep pretty printer in struct - why? why not?
|
116
|
-
|
117
|
-
|
118
|
-
## add standings table in markdown to buffer (buf)
|
119
|
-
|
120
|
-
## todo: use different styles/formats (simple/ etc ???)
|
121
|
-
|
122
|
-
## simple table (only totals - no home/away)
|
123
|
-
## standings.to_a.each do |l|
|
124
|
-
## buf << '%2d. ' % l.rank
|
125
|
-
## buf << '%-28s ' % l.team
|
126
|
-
## buf << '%2d ' % l.played
|
127
|
-
## buf << '%3d ' % l.won
|
128
|
-
## buf << '%3d ' % l.drawn
|
129
|
-
## buf << '%3d ' % l.lost
|
130
|
-
## buf << '%3d:%-3d ' % [l.goals_for,l.goals_against]
|
131
|
-
## buf << '%3d' % l.pts
|
132
|
-
## buf << "\n"
|
133
|
-
## end
|
134
|
-
|
135
|
-
buf = ''
|
136
|
-
buf << "\n"
|
137
|
-
buf << "```\n"
|
138
|
-
buf << " - Home - - Away - - Total -\n"
|
139
|
-
buf << " Pld W D L F:A W D L F:A F:A +/- Pts\n"
|
140
|
-
|
141
|
-
to_a.each do |l|
|
142
|
-
buf << '%2d. ' % l.rank
|
143
|
-
buf << '%-28s ' % l.team
|
144
|
-
buf << '%2d ' % l.played
|
145
|
-
|
146
|
-
buf << '%2d ' % l.home_won
|
147
|
-
buf << '%2d ' % l.home_drawn
|
148
|
-
buf << '%2d ' % l.home_lost
|
149
|
-
buf << '%3d:%-3d ' % [l.home_goals_for,l.home_goals_against]
|
150
|
-
|
151
|
-
buf << '%2d ' % l.away_won
|
152
|
-
buf << '%2d ' % l.away_drawn
|
153
|
-
buf << '%2d ' % l.away_lost
|
154
|
-
buf << '%3d:%-3d ' % [l.away_goals_for,l.away_goals_against]
|
155
|
-
|
156
|
-
buf << '%3d:%-3d ' % [l.goals_for,l.goals_against]
|
157
|
-
|
158
|
-
goals_diff = l.goals_for-l.goals_against
|
159
|
-
if goals_diff > 0
|
160
|
-
buf << '%3s ' % "+#{goals_diff}"
|
161
|
-
elsif goals_diff < 0
|
162
|
-
buf << '%3s ' % "#{goals_diff}"
|
163
|
-
else ## assume 0
|
164
|
-
buf << ' '
|
165
|
-
end
|
166
|
-
|
167
|
-
buf << '%3d' % l.pts
|
168
|
-
buf << "\n"
|
169
|
-
end
|
170
|
-
|
171
|
-
buf << "```\n"
|
172
|
-
buf << "\n"
|
173
|
-
|
174
|
-
## optinal: add data source if known / present
|
175
|
-
## assume (relative) markdown link for now in README.md
|
176
|
-
if source
|
177
|
-
buf << "(Source: [`#{source}`](#{source}))\n"
|
178
|
-
buf << "\n"
|
179
|
-
end
|
180
|
-
|
181
|
-
buf
|
182
|
-
end
|
183
|
-
|
184
|
-
|
185
|
-
private
|
186
|
-
def update_match( m ) ## add a match
|
187
|
-
|
188
|
-
## note: always use team as string for now
|
189
|
-
## for now allow passing in of string OR struct - why? why not?
|
190
|
-
## todo/fix: change to m.team1_name and m.team2_name - why? why not?
|
191
|
-
team1 = m.team1.is_a?( String ) ? m.team1 : m.team1.name
|
192
|
-
team2 = m.team2.is_a?( String ) ? m.team2 : m.team2.name
|
193
|
-
|
194
|
-
score = m.score.to_s
|
195
|
-
|
196
|
-
## puts " #{team1} - #{team2} #{score}"
|
197
|
-
|
198
|
-
unless m.over?
|
199
|
-
puts " !!!! skipping match - not yet over (date in the future) => #{m.date}"
|
200
|
-
return
|
201
|
-
end
|
202
|
-
|
203
|
-
unless m.complete?
|
204
|
-
puts "!!! [calc_standings] skipping match #{team1} - #{team2} w/ past date #{m.date} - scores incomplete => #{score}"
|
205
|
-
return
|
206
|
-
end
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
line1 = @lines[ team1 ] || StandingsLine.new( team1 )
|
211
|
-
line2 = @lines[ team2 ] || StandingsLine.new( team2 )
|
212
|
-
|
213
|
-
line1.played += 1
|
214
|
-
line1.home_played += 1
|
215
|
-
|
216
|
-
line2.played += 1
|
217
|
-
line2.away_played += 1
|
218
|
-
|
219
|
-
if m.winner == 1
|
220
|
-
line1.won += 1
|
221
|
-
line1.home_won += 1
|
222
|
-
|
223
|
-
line2.lost += 1
|
224
|
-
line2.away_lost += 1
|
225
|
-
|
226
|
-
line1.pts += @pts_won
|
227
|
-
line1.home_pts += @pts_won
|
228
|
-
elsif m.winner == 2
|
229
|
-
line1.lost += 1
|
230
|
-
line1.home_lost += 1
|
231
|
-
|
232
|
-
line2.won += 1
|
233
|
-
line2.away_won += 1
|
234
|
-
|
235
|
-
line2.pts += @pts_won
|
236
|
-
line2.away_pts += @pts_won
|
237
|
-
else ## assume drawn/tie (that is, 0)
|
238
|
-
line1.drawn += 1
|
239
|
-
line1.home_drawn += 1
|
240
|
-
|
241
|
-
line2.drawn += 1
|
242
|
-
line2.away_drawn += 1
|
243
|
-
|
244
|
-
line1.pts += 1
|
245
|
-
line1.home_pts += 1
|
246
|
-
line2.pts += 1
|
247
|
-
line2.away_pts += 1
|
248
|
-
end
|
249
|
-
|
250
|
-
if m.score1 && m.score2
|
251
|
-
line1.goals_for += m.score1
|
252
|
-
line1.home_goals_for += m.score1
|
253
|
-
line1.goals_against += m.score2
|
254
|
-
line1.home_goals_against += m.score2
|
255
|
-
|
256
|
-
line2.goals_for += m.score2
|
257
|
-
line2.away_goals_for += m.score2
|
258
|
-
line2.goals_against += m.score1
|
259
|
-
line2.away_goals_against += m.score1
|
260
|
-
else
|
261
|
-
puts "*** warn: [standings] skipping match with missing scores: #{m.inspect}"
|
262
|
-
end
|
263
|
-
|
264
|
-
@lines[ team1 ] = line1
|
265
|
-
@lines[ team2 ] = line2
|
266
|
-
end # method update_match
|
267
|
-
|
268
|
-
end # class Standings
|
269
|
-
|
270
|
-
|
271
|
-
end # module Sports
|
data/lib/sports/structs/team.rb
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
|
2
|
-
module Sports
|
3
|
-
|
4
|
-
##
|
5
|
-
## todo/fix: remove self.create in structs!!! use just new!!!
|
6
|
-
|
7
|
-
class Team
|
8
|
-
## todo: use just names for alt_names - why? why not?
|
9
|
-
attr_accessor :key, :name, :alt_names,
|
10
|
-
:code, ## code == abbreviation e.g. ARS etc.
|
11
|
-
:year, :year_end, ## todo/fix: change year to start_year and year_end to end_year (like in season)!!!
|
12
|
-
:country
|
13
|
-
|
14
|
-
|
15
|
-
def names
|
16
|
-
## todo/check: add alt_names_auto too? - why? why not?
|
17
|
-
[@name] + @alt_names
|
18
|
-
end ## all names
|
19
|
-
|
20
|
-
def key
|
21
|
-
## note: auto-generate key "on-the-fly" if missing for now - why? why not?
|
22
|
-
## note: quick hack - auto-generate key, that is, remove all non-ascii chars and downcase
|
23
|
-
@key || @name.downcase.gsub( /[^a-z]/, '' )
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
## special import only attribs
|
28
|
-
attr_accessor :alt_names_auto ## auto-generated alt names
|
29
|
-
attr_accessor :wikipedia # wikipedia page name (for english (en))
|
30
|
-
|
31
|
-
|
32
|
-
def historic?() @year_end ? true : false; end
|
33
|
-
alias_method :past?, :historic?
|
34
|
-
|
35
|
-
def wikipedia?() @wikipedia; end
|
36
|
-
def wikipedia_url
|
37
|
-
if @wikipedia
|
38
|
-
## note: replace spaces with underscore (-)
|
39
|
-
## e.g. Club Brugge KV => Club_Brugge_KV
|
40
|
-
## todo/check/fix:
|
41
|
-
## check if "plain" dash (-) needs to get replaced with typographic dash??
|
42
|
-
"https://en.wikipedia.org/wiki/#{@wikipedia.gsub(' ','_')}"
|
43
|
-
else
|
44
|
-
nil
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
def initialize( **kwargs )
|
50
|
-
@alt_names = []
|
51
|
-
@alt_names_auto = []
|
52
|
-
|
53
|
-
update( kwargs ) unless kwargs.empty?
|
54
|
-
end
|
55
|
-
|
56
|
-
def update( **kwargs )
|
57
|
-
@key = kwargs[:key] if kwargs.has_key? :key
|
58
|
-
@name = kwargs[:name] if kwargs.has_key? :name
|
59
|
-
@code = kwargs[:code] if kwargs.has_key? :code
|
60
|
-
@alt_names = kwargs[:alt_names] if kwargs.has_key? :alt_names
|
61
|
-
self ## note - MUST return self for chaining
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
##############################
|
67
|
-
## helper methods for import only??
|
68
|
-
## check for duplicates
|
69
|
-
include SportDb::NameHelper
|
70
|
-
|
71
|
-
def duplicates?
|
72
|
-
names = [name] + alt_names + alt_names_auto
|
73
|
-
names = names.map { |name| normalize( sanitize(name) ) }
|
74
|
-
|
75
|
-
names.size != names.uniq.size
|
76
|
-
end
|
77
|
-
|
78
|
-
def duplicates
|
79
|
-
names = [name] + alt_names + alt_names_auto
|
80
|
-
|
81
|
-
## calculate (count) frequency and select if greater than one
|
82
|
-
names.reduce( {} ) do |h,name|
|
83
|
-
norm = normalize( sanitize(name) )
|
84
|
-
h[norm] ||= []
|
85
|
-
h[norm] << name; h
|
86
|
-
end.select { |norm,names| names.size > 1 }
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
def add_variants( name_or_names )
|
91
|
-
names = name_or_names.is_a?(Array) ? name_or_names : [name_or_names]
|
92
|
-
names.each do |name|
|
93
|
-
name = sanitize( name )
|
94
|
-
self.alt_names_auto += variants( name )
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end # class Team
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
class NationalTeam < Team
|
102
|
-
def initialize( **kwargs )
|
103
|
-
super
|
104
|
-
end
|
105
|
-
|
106
|
-
def update( **kwargs )
|
107
|
-
super
|
108
|
-
self ## note - MUST return self for chaining
|
109
|
-
end
|
110
|
-
|
111
|
-
end # class NationalTeam
|
112
|
-
|
113
|
-
|
114
|
-
########
|
115
|
-
# more attribs - todo/fix - also add "upstream" to struct & model!!!!!
|
116
|
-
# district, geos, year_end, country, etc.
|
117
|
-
|
118
|
-
class Club < Team
|
119
|
-
attr_accessor :ground
|
120
|
-
|
121
|
-
attr_accessor :a, :b
|
122
|
-
def a?() @a == nil; end ## is a (1st) team / club (i)? if a is NOT set
|
123
|
-
def b?() @a != nil; end ## is b (2nd/reserve/jr) team / club (ii) if a is set
|
124
|
-
|
125
|
-
## note: delegate/forward all geo attributes for team b for now (to team a) - keep - why? why not?
|
126
|
-
attr_writer :city, :district, :geos
|
127
|
-
def city() @a == nil ? @city : @a.city; end
|
128
|
-
def district() @a == nil ? @district : @a.district; end
|
129
|
-
def country() @a == nil ? @country : @a.country; end
|
130
|
-
def geos() @a == nil ? @geos : @a.geos; end
|
131
|
-
|
132
|
-
|
133
|
-
def initialize( **kwargs )
|
134
|
-
super
|
135
|
-
end
|
136
|
-
|
137
|
-
def update( **kwargs )
|
138
|
-
super
|
139
|
-
@city = kwargs[:city] if kwargs.has_key? :city
|
140
|
-
## todo/fix: use city struct - why? why not?
|
141
|
-
## todo/fix: add country too or report unused keywords / attributes - why? why not?
|
142
|
-
|
143
|
-
self ## note - MUST return self for chaining
|
144
|
-
end
|
145
|
-
end # class Club
|
146
|
-
|
147
|
-
end # module Sports
|