sports 0.0.1 → 0.1.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: 9ff86ce7425d973d8bb3adc8235bc0889c050fbd
4
- data.tar.gz: 82decba8bbf6f6964612ca0913086224abfdeb1f
3
+ metadata.gz: 831dacee645acf5dea757570924ad7c9e1e2212f
4
+ data.tar.gz: 676b1114696241c5113854fc523821da2a8155ad
5
5
  SHA512:
6
- metadata.gz: '039b38b1e98a51a30390b88dbda7d35035134938af42767f96c7cf0ecf043c70a6ec2c58a99f59e9f0fe98a636755bbff8310def3431ada51a4a142dd668e956'
7
- data.tar.gz: fe78d06e9c4aa9a44ef1bcc26859da1884fa191b1ea4a28d3bd059c2198f91c875a4182b3a65aa569591ef47e0db92123cc96f5ec331e9fe832fb2a7226bf321
6
+ metadata.gz: 61ad3469162b1c9269166ec3e4ed8fa739be3e181e7e0fa74bd3440ef631f046a293fe63752c4c28d9f5c4a856ddc83e6f15b94b347c3c50b773adb2b4b98768
7
+ data.tar.gz: 3cde3acf5f4a7756f0b68d5c4686f374f6cf45f967f4070c8b952e7747e0bf51ebb32d464a4e71b9e887dcac40b7b9ac12e1f6cf3aa1cdb8c270982d2ba73d75
@@ -4,6 +4,7 @@ README.md
4
4
  Rakefile
5
5
  lib/sports.rb
6
6
  lib/sports/config.rb
7
+ lib/sports/goal_parser_csv.rb
7
8
  lib/sports/match_parser_csv.rb
8
9
  lib/sports/match_status_parser.rb
9
10
  lib/sports/name_helper.rb
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # sports - sport data structures for matches, scores, leagues, seasons, rounds, groups, teams, clubs and more"
1
+ # sports - sport data structures for matches, scores, leagues, seasons, rounds, groups, teams, clubs and more
2
2
 
3
3
 
4
4
  * home :: [github.com/sportdb/sport.db](https://github.com/sportdb/sport.db)
@@ -11,7 +11,285 @@
11
11
 
12
12
  ## Usage
13
13
 
14
- To be done
14
+
15
+ ### Working with CSV Datafiles
16
+
17
+ Let's build the standings table for the English Premier League (EPL) for the 2019/20 season from all matches
18
+ in the comma-separated values (.csv) format.
19
+ Example:
20
+
21
+ ```
22
+ - Home - - Away - - Total -
23
+ Pld W D L F:A W D L F:A F:A +/- Pts
24
+ 1. Liverpool FC 38 18 1 0 52:16 14 2 3 33:17 85:33 +52 99
25
+ 2. Manchester City FC 38 15 2 2 57:13 11 1 7 45:22 102:35 +67 81
26
+ 3. Manchester United FC 38 10 7 2 40:17 8 5 6 26:19 66:36 +30 66
27
+ 4. Chelsea FC 38 11 3 5 30:16 9 3 7 39:38 69:54 +15 66
28
+ 5. Leicester City FC 38 11 4 4 35:17 7 4 8 32:24 67:41 +26 62
29
+ 6. Tottenham Hotspur FC 38 12 3 4 36:17 4 8 7 25:30 61:47 +14 59
30
+ 7. Wolverhampton Wanderers FC 38 8 7 4 27:19 7 7 5 24:21 51:40 +11 59
31
+ 8. Arsenal FC 38 10 6 3 36:24 4 8 7 20:24 56:48 +8 56
32
+ 9. Sheffield United FC 38 10 3 6 24:15 4 9 6 15:24 39:39 54
33
+ 10. Burnley FC 38 8 4 7 24:23 7 5 7 19:27 43:50 -7 54
34
+ 11. Southampton FC 38 6 3 10 21:35 9 4 6 30:25 51:60 -9 52
35
+ 12. Everton FC 38 8 7 4 24:21 5 3 11 20:35 44:56 -12 49
36
+ 13. Newcastle United FC 38 6 8 5 20:21 5 3 11 18:37 38:58 -20 44
37
+ 14. Crystal Palace FC 38 6 5 8 15:20 5 5 9 16:30 31:50 -19 43
38
+ 15. Brighton & Hove Albion FC 38 5 7 7 20:27 4 7 8 19:27 39:54 -15 41
39
+ 16. West Ham United FC 38 6 4 9 30:33 4 5 10 19:29 49:62 -13 39
40
+ 17. Aston Villa FC 38 7 3 9 22:30 2 5 12 19:37 41:67 -26 35
41
+ 18. AFC Bournemouth 38 5 6 8 22:30 4 1 14 18:35 40:65 -25 34
42
+ 19. Watford FC 38 6 6 7 22:27 2 4 13 14:37 36:64 -28 34
43
+ 20. Norwich City FC 38 4 3 12 19:37 1 3 15 7:38 26:75 -49 21
44
+ ```
45
+
46
+
47
+ Step 0: Download the free public domain [`/england`](https://github.com/footballcsv/england) dataset package / archive from the [football.csv org](https://footballcsv.github.io).
48
+
49
+ What's the comma-separated values (.csv) format?
50
+ It's the world's most popular tabular data interchange format in text.
51
+ Values are separated - surprise, surprise - by commas (that is, `,`).
52
+ One line is one record, that is, one match :-).
53
+ Example:
54
+
55
+ ```
56
+ Team 1, FT, HT, Team 2
57
+ Liverpool FC, 4-1, 4-0, Norwich City FC
58
+ West Ham United FC, 0-5, 0-1, Manchester City FC
59
+ AFC Bournemouth, 1-1, 0-0, Sheffield United FC
60
+ Burnley FC, 3-0, 0-0, Southampton FC
61
+ Crystal Palace FC, 0-0, 0-0, Everton FC
62
+ Watford FC, 0-3, 0-1, Brighton & Hove Albion FC
63
+ Tottenham Hotspur FC, 3-1, 0-1, Aston Villa FC
64
+ Leicester City FC, 0-0, 0-0, Wolverhampton Wanderers FC
65
+ Newcastle United FC, 0-1, 0-0, Arsenal FC
66
+ Manchester United FC, 4-0, 1-0, Chelsea FC
67
+ ...
68
+ ```
69
+
70
+ Let's use the [`eng.1.csv`](https://github.com/footballcsv/england/blob/master/2010s/2019-20/eng.1.csv), that is, the English Premier League datafile in the `/2010s/2019-20` folder.
71
+ Read in all matches with the `Match.read_csv` method from the sports library / gem:
72
+
73
+
74
+ ``` ruby
75
+ require 'sports'
76
+
77
+ include Sports ## include Match, Team, Standings, etc. data classes
78
+
79
+
80
+ matches = Match.read_csv( 'england/2010s/2019-20/eng.1.csv' )
81
+ pp matches.size #=> 380
82
+ pp matches
83
+ ```
84
+
85
+ will pretty print (pp):
86
+
87
+ ```
88
+ [#<Sports::Match
89
+ @date = "2019-08-09",
90
+ @score1 = 4,
91
+ @score1i = 4,
92
+ @score2 = 1,
93
+ @score2i = 0,
94
+ @team1 = "Liverpool FC",
95
+ @team2 = "Norwich City FC">,
96
+ #<Sports::Match
97
+ @date = "2019-08-10",
98
+ @score1 = 0,
99
+ @score1i = 0,
100
+ @score2 = 5,
101
+ @score2i = 1,
102
+ @team1 = "West Ham United FC",
103
+ @team2 = "Manchester City FC">,
104
+ ...]
105
+ ```
106
+
107
+ Now tally up all matches for the standings table with the standings helper class:
108
+
109
+ ``` ruby
110
+ standings = Standings.new( matches )
111
+ pp standings
112
+ ```
113
+
114
+ will pretty print (pp):
115
+
116
+ ```
117
+ #<Sports::Standings @lines=[
118
+ #<Sports::StandingsLine
119
+ @away_drawn = 0,
120
+ @away_goals_against = 1,
121
+ @away_goals_for = 5,
122
+ @away_lost = 0,
123
+ @away_played = 2,
124
+ @away_pts = 6,
125
+ @away_won = 2,
126
+ @drawn = 0,
127
+ @goals_against = 3,
128
+ @goals_for = 12,
129
+ @home_drawn = 0,
130
+ @home_goals_against = 2,
131
+ @home_goals_for = 7,
132
+ @home_lost = 0,
133
+ @home_played = 2,
134
+ @home_pts = 6,
135
+ @home_won = 2,
136
+ @lost = 0,
137
+ @played = 4,
138
+ @pts = 12,
139
+ @rank = 1,
140
+ @team = "Liverpool FC",
141
+ @won = 4>,
142
+ #<Sports::StandingsLine
143
+ @away_drawn = 0,
144
+ @away_goals_against = 6,
145
+ @away_goals_for = 1,
146
+ @away_lost = 2,
147
+ @away_played = 2,
148
+ @away_pts = 0,
149
+ @away_won = 0,
150
+ @drawn = 0,
151
+ @goals_against = 10,
152
+ @goals_for = 6,
153
+ @home_drawn = 0,
154
+ @home_goals_against = 4,
155
+ @home_goals_for = 5,
156
+ @home_lost = 1,
157
+ @home_played = 2,
158
+ @home_pts = 3,
159
+ @home_won = 1,
160
+ @lost = 3,
161
+ @played = 4,
162
+ @pts = 3,
163
+ @rank = 19,
164
+ @team = "Norwich City FC",
165
+ @won = 1>,
166
+ ... ]>
167
+ ```
168
+
169
+ Now let's format the standings for humans :-) in the classic more compact table format.
170
+ Let's start with
171
+ showing only totals - no home/away break outs yet -
172
+ in the first simple version:
173
+
174
+ ``` ruby
175
+ standings.each do |l|
176
+ print '%2d. ' % l.rank
177
+ print '%-28s ' % l.team
178
+ print '%2d ' % l.played
179
+ print '%3d ' % l.won
180
+ print '%3d ' % l.drawn
181
+ print '%3d ' % l.lost
182
+ print '%3d:%-3d ' % [l.goals_for,l.goals_against]
183
+ print '%3d' % l.pts
184
+ print "\n"
185
+ end
186
+ ```
187
+
188
+ resulting in:
189
+
190
+ ```
191
+ 1. Liverpool FC 38 32 3 3 85:33 99
192
+ 2. Manchester City FC 38 26 3 9 102:35 81
193
+ 3. Manchester United FC 38 18 12 8 66:36 66
194
+ 4. Chelsea FC 38 20 6 12 69:54 66
195
+ 5. Leicester City FC 38 18 8 12 67:41 62
196
+ 6. Tottenham Hotspur FC 38 16 11 11 61:47 59
197
+ 7. Wolverhampton Wanderers FC 38 15 14 9 51:40 59
198
+ 8. Arsenal FC 38 14 14 10 56:48 56
199
+ 9. Sheffield United FC 38 14 12 12 39:39 54
200
+ 10. Burnley FC 38 15 9 14 43:50 54
201
+ 11. Southampton FC 38 15 7 16 51:60 52
202
+ 12. Everton FC 38 13 10 15 44:56 49
203
+ 13. Newcastle United FC 38 11 11 16 38:58 44
204
+ 14. Crystal Palace FC 38 11 10 17 31:50 43
205
+ 15. Brighton & Hove Albion FC 38 9 14 15 39:54 41
206
+ 16. West Ham United FC 38 10 9 19 49:62 39
207
+ 17. Aston Villa FC 38 9 8 21 41:67 35
208
+ 18. AFC Bournemouth 38 9 7 22 40:65 34
209
+ 19. Watford FC 38 8 10 20 36:64 34
210
+ 20. Norwich City FC 38 5 6 27 26:75 21
211
+ ```
212
+
213
+ To wrap up let's break out the standings for home and away matches for a deluxe version:
214
+
215
+ ``` ruby
216
+ print " - Home - - Away - - Total -\n"
217
+ print " Pld W D L F:A W D L F:A F:A +/- Pts\n"
218
+
219
+ standings.each do |l|
220
+ print '%2d. ' % l.rank
221
+ print '%-28s ' % l.team
222
+ print '%2d ' % l.played
223
+
224
+ print '%2d ' % l.home_won
225
+ print '%2d ' % l.home_drawn
226
+ print '%2d ' % l.home_lost
227
+ print '%3d:%-3d ' % [l.home_goals_for,l.home_goals_against]
228
+
229
+ print '%2d ' % l.away_won
230
+ print '%2d ' % l.away_drawn
231
+ print '%2d ' % l.away_lost
232
+ print '%3d:%-3d ' % [l.away_goals_for,l.away_goals_against]
233
+
234
+ print '%3d:%-3d ' % [l.goals_for,l.goals_against]
235
+
236
+ goals_diff = l.goals_for-l.goals_against
237
+ if goals_diff > 0
238
+ print '%3s ' % "+#{goals_diff}"
239
+ elsif goals_diff < 0
240
+ print '%3s ' % "#{goals_diff}"
241
+ else ## assume 0
242
+ print ' '
243
+ end
244
+
245
+ print '%3d' % l.pts
246
+ print "\n"
247
+ end
248
+ ```
249
+
250
+ resulting in:
251
+
252
+ ```
253
+ - Home - - Away - - Total -
254
+ Pld W D L F:A W D L F:A F:A +/- Pts
255
+ 1. Liverpool FC 38 18 1 0 52:16 14 2 3 33:17 85:33 +52 99
256
+ 2. Manchester City FC 38 15 2 2 57:13 11 1 7 45:22 102:35 +67 81
257
+ 3. Manchester United FC 38 10 7 2 40:17 8 5 6 26:19 66:36 +30 66
258
+ 4. Chelsea FC 38 11 3 5 30:16 9 3 7 39:38 69:54 +15 66
259
+ 5. Leicester City FC 38 11 4 4 35:17 7 4 8 32:24 67:41 +26 62
260
+ 6. Tottenham Hotspur FC 38 12 3 4 36:17 4 8 7 25:30 61:47 +14 59
261
+ 7. Wolverhampton Wanderers FC 38 8 7 4 27:19 7 7 5 24:21 51:40 +11 59
262
+ 8. Arsenal FC 38 10 6 3 36:24 4 8 7 20:24 56:48 +8 56
263
+ 9. Sheffield United FC 38 10 3 6 24:15 4 9 6 15:24 39:39 54
264
+ 10. Burnley FC 38 8 4 7 24:23 7 5 7 19:27 43:50 -7 54
265
+ 11. Southampton FC 38 6 3 10 21:35 9 4 6 30:25 51:60 -9 52
266
+ 12. Everton FC 38 8 7 4 24:21 5 3 11 20:35 44:56 -12 49
267
+ 13. Newcastle United FC 38 6 8 5 20:21 5 3 11 18:37 38:58 -20 44
268
+ 14. Crystal Palace FC 38 6 5 8 15:20 5 5 9 16:30 31:50 -19 43
269
+ 15. Brighton & Hove Albion FC 38 5 7 7 20:27 4 7 8 19:27 39:54 -15 41
270
+ 16. West Ham United FC 38 6 4 9 30:33 4 5 10 19:29 49:62 -13 39
271
+ 17. Aston Villa FC 38 7 3 9 22:30 2 5 12 19:37 41:67 -26 35
272
+ 18. AFC Bournemouth 38 5 6 8 22:30 4 1 14 18:35 40:65 -25 34
273
+ 19. Watford FC 38 6 6 7 22:27 2 4 13 14:37 36:64 -28 34
274
+ 20. Norwich City FC 38 4 3 12 19:37 1 3 15 7:38 26:75 -49 21
275
+ ```
276
+
277
+
278
+ That's it.
279
+ Bonus: Why not build a standings table for the Bundesliga, La Liga, Ligue 1, Seria A, or your very own league? Yes, you can.
280
+
281
+
282
+
283
+ ## Installation
284
+
285
+ Use
286
+
287
+ gem install sports
288
+
289
+ or add the gem to your Gemfile
290
+
291
+ gem 'sports'
292
+
15
293
 
16
294
  ## License
17
295
 
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require './lib/sports/version.rb'
3
3
 
4
4
  Hoe.spec 'sports' do
5
5
 
6
- self.version = Sports::VERSION
6
+ self.version = SportDb::Module::Sports::VERSION
7
7
 
8
8
  self.summary = "sports - sport data structures for matches, scores, leagues, seasons, rounds, groups, teams, clubs and more"
9
9
  self.description = summary
@@ -22,7 +22,7 @@ Hoe.spec 'sports' do
22
22
  self.extra_deps = [
23
23
  ['alphabets', '>= 1.0.0'],
24
24
  ['date-formats', '>= 1.0.1'],
25
- ['score-formats', '>= 0.0.1'],
25
+ ['score-formats', '>= 0.1.0'],
26
26
  ['csvreader', '>= 1.2.4'],
27
27
  ]
28
28
 
@@ -46,10 +46,37 @@ require 'sports/structs/team_usage'
46
46
 
47
47
  require 'sports/match_status_parser'
48
48
  require 'sports/match_parser_csv'
49
-
50
-
51
-
52
-
53
- puts Sports.banner # say hello
49
+ require 'sports/goal_parser_csv'
50
+
51
+
52
+ ### add convenience shortcut helpers
53
+ module Sports
54
+ class Match
55
+ def self.read_csv( path, headers: nil, filters: nil, converters: nil, sep: nil )
56
+ SportDb::CsvMatchParser.read( path,
57
+ headers: headers,
58
+ filters: filters,
59
+ converters: converters,
60
+ sep: sep )
61
+ end
62
+
63
+ def self.parse_csv( txt, headers: nil, filters: nil, converters: nil, sep: nil )
64
+ SportDb::CsvMatchParser.parse( txt,
65
+ headers: headers,
66
+ filters: filters,
67
+ converters: converters,
68
+ sep: sep )
69
+ end
70
+ end # class Match
71
+ end # module Sports
72
+
73
+
74
+ #####
75
+ # note: add Sport and Football convenience alias - why? why not?
76
+
77
+ Sport = Sports
78
+ Football = Sports
79
+
80
+ puts SportDb::Module::Sports.banner # say hello
54
81
 
55
82
 
@@ -1,25 +1,40 @@
1
1
 
2
- module Sports
3
-
4
- class Configuration
5
-
6
- attr_reader :lang
7
- def lang=(value)
8
- ## check/todo: always use to_sym - why? needed?
9
- DateFormats.lang = value
10
- ScoreFormats.lang = value
2
+ module SportDb
3
+ module Import
4
+ class Configuration
5
+
6
+ attr_reader :lang
7
+ def lang=(value)
8
+ ## check/todo: always use to_sym - why? needed?
9
+ DateFormats.lang = value
10
+ ScoreFormats.lang = value
11
+ end
12
+
13
+ ## lets you use
14
+ ## SportDb.configure do |config|
15
+ ## config.lang = 'it'
16
+ ## end
17
+
18
+ def self.configure() yield( config ); end
19
+
20
+ end # class Configuration
21
+ def self.config() @config ||= Configuration.new; end
11
22
  end
23
+ end
12
24
 
13
- end # class Configuration
14
25
 
15
26
 
27
+ module Sports
28
+
16
29
  ## lets you use
17
30
  ## Sports.configure do |config|
18
31
  ## config.lang = 'it'
19
32
  ## end
20
33
 
34
+ ## note: just forward to SportDb::Import configuration!!!!!
35
+ ## keep Sports module / namespace "clean"
36
+ ## that is, only include data structures (e.g. Match,League,etc) for now - why? why not?
21
37
  def self.configure() yield( config ); end
22
-
23
- def self.config() @config ||= Configuration.new; end
38
+ def self.config() SportDb::Import.config; end
24
39
 
25
40
  end # module Sports
@@ -0,0 +1,28 @@
1
+
2
+ module SportDb
3
+ class CsvGoalParser
4
+
5
+
6
+ def self.read( path )
7
+ txt = File.open( path, 'r:utf-8' ) {|f| f.read } ## note: make sure to use (assume) utf-8
8
+ parse( txt )
9
+ end
10
+
11
+ def self.parse( txt )
12
+ new( txt ).parse
13
+ end
14
+
15
+
16
+ def initialize( txt )
17
+ @txt = txt
18
+ end
19
+
20
+ def parse
21
+ rows = parse_csv( @txt )
22
+ recs = rows.map { |row| Sports::GoalEvent.build( row ) }
23
+ ## pp recs[0]
24
+ recs
25
+ end
26
+
27
+ end # class CsvGoalParser
28
+ end # module Sports
@@ -1,5 +1,5 @@
1
1
 
2
- module Sports
2
+ module SportDb
3
3
  class CsvMatchParser
4
4
 
5
5
  #############
@@ -88,6 +88,7 @@ module Sports
88
88
  headers_mapping[:team1] = find_header( headers, ['Team 1'] )
89
89
  headers_mapping[:team2] = find_header( headers, ['Team 2'] )
90
90
  headers_mapping[:date] = find_header( headers, ['Date'] )
91
+ headers_mapping[:time] = find_header( headers, ['Time'] )
91
92
 
92
93
  ## check for all-in-one full time (ft) and half time (ht9 scores?
93
94
  headers_mapping[:score] = find_header( headers, ['FT'] )
@@ -120,6 +121,7 @@ module Sports
120
121
  headers_mapping[:team1] = find_header( headers, ['HomeTeam', 'HT', 'Home'] )
121
122
  headers_mapping[:team2] = find_header( headers, ['AwayTeam', 'AT', 'Away'] )
122
123
  headers_mapping[:date] = find_header( headers, ['Date'] )
124
+ headers_mapping[:time] = find_header( headers, ['Time'] )
123
125
 
124
126
  ## note: FT = Full Time, HG = Home Goal, AG = Away Goal
125
127
  headers_mapping[:score1] = find_header( headers, ['FTHG', 'HG'] )
@@ -197,6 +199,37 @@ module Sports
197
199
 
198
200
 
199
201
 
202
+ col = row[ headers_mapping[ :time ]]
203
+
204
+ if col.nil?
205
+ time = nil
206
+ else
207
+ col = col.strip # make sure not leading or trailing spaces left over
208
+
209
+ if col.empty?
210
+ col =~ /^-{1,}$/ || # e.g. - or ---
211
+ col =~ /^\?{1,}$/ # e.g. ? or ???
212
+ ## note: allow missing / unknown date for match
213
+ time = nil
214
+ else
215
+ if col =~ /^\d{1,2}:\d{2}$/
216
+ time_fmt = '%H:%M' # e.g. 17:00 or 3:00
217
+ elsif col =~ /^\d{1,2}.\d{2}$/
218
+ time_fmt = '%H.%M' # e.g. 17:00 or 3:00
219
+ else
220
+ puts "*** !!! wrong (unknown) time format >>#{col}<<; cannot continue; fix it; sorry"
221
+ ## todo/fix: add to errors/warns list - why? why not?
222
+ exit 1
223
+ end
224
+
225
+ ## todo/check: use date object (keep string?) - why? why not?
226
+ ## todo/fix: yes!! use date object!!!! do NOT use string
227
+ time = Time.strptime( col, time_fmt ).strftime( '%H:%M' )
228
+ end
229
+ end
230
+
231
+
232
+
200
233
  col = row[ headers_mapping[ :date ]]
201
234
  col = col.strip # make sure not leading or trailing spaces left over
202
235
 
@@ -387,6 +420,7 @@ module Sports
387
420
  ## puts 'match attributes:'
388
421
  attributes = {
389
422
  date: date,
423
+ time: time,
390
424
  team1: team1, team2: team2,
391
425
  score1: score1, score2: score2,
392
426
  score1i: score1i, score2i: score2i,
@@ -400,7 +434,7 @@ module Sports
400
434
  }
401
435
  ## pp attributes
402
436
 
403
- match = Match.new( **attributes )
437
+ match = Sports::Match.new( **attributes )
404
438
  matches << match
405
439
  end
406
440
 
@@ -451,6 +485,6 @@ end # method parse_score
451
485
 
452
486
 
453
487
 
454
- end # class CsvMatchParser
488
+ end # class CsvMatchParser
455
489
  end # module Sports
456
490
 
@@ -7,7 +7,10 @@
7
7
  # etc.
8
8
 
9
9
 
10
- module Sports
10
+ module SportDb
11
+
12
+
13
+ ### todo/fix: move Status inside Match struct - why? why not?
11
14
 
12
15
  class Status
13
16
  # note: use a class as an "enum"-like namespace for now - why? why not?
@@ -81,7 +84,7 @@ module Sports
81
84
 
82
85
  status
83
86
  end # method find!
84
- end # class StatusParser
87
+ end # class StatusParser
85
88
 
86
- end # module Sports
89
+ end # module SportDb
87
90
 
@@ -1,5 +1,5 @@
1
1
 
2
- module Sports
2
+ module SportDb
3
3
  module NameHelper
4
4
 
5
5
 
@@ -85,7 +85,7 @@ class Season
85
85
  :end_year
86
86
 
87
87
  def initialize( *args ) ## change args to years - why? why not?
88
- if args.size == 1 && args[0].is_a?( Integer )
88
+ if args.size == 1 && args[0].is_a?( Integer )
89
89
  @start_year = args[0]
90
90
  @end_year = args[0]
91
91
  elsif args.size == 2 && args[0].is_a?( Integer ) &&
@@ -47,8 +47,8 @@ class GoalEvent
47
47
  )?
48
48
  ['.]
49
49
  $}x.match( row['Minute'])
50
- minute = $1.to_i
51
- offset = $2 ? $2.to_i : nil
50
+ minute = m[1].to_i
51
+ offset = m[2] ? m[2].to_i : nil
52
52
  else
53
53
  puts "!! ERROR - unsupported minute (goal) format >#{row['Minute']}<"
54
54
  exit 1
@@ -174,8 +174,8 @@ class Goal ### nested (non-freestanding) inside match (match is parent)
174
174
  minute: event.minute,
175
175
  offset: event.offset,
176
176
  player: event.player,
177
- owngoal: event.owngoal?,
178
- penalty: event.penalty?,
177
+ owngoal: event.owngoal,
178
+ penalty: event.penalty,
179
179
  notes: event.notes
180
180
  }
181
181
 
@@ -5,6 +5,7 @@ module Sports
5
5
  class Match
6
6
 
7
7
  attr_reader :date,
8
+ :time,
8
9
  :team1, :team2, ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
9
10
  :score1, :score2, ## full time
10
11
  :score1i, :score2i, ## half time (first (i) part)
@@ -26,12 +27,20 @@ class Match
26
27
  attr_accessor :goals ## todo/fix: make goals like all other attribs!!
27
28
 
28
29
  def initialize( **kwargs )
30
+ @score1 = @score2 = nil ## full time
31
+ @score1i = @score2i = nil ## half time (first (i) part)
32
+ @score1et = @score2et = nil ## extra time
33
+ @score1p = @score2p = nil ## penalty
34
+ @score1agg = @score2agg = nil ## full time (all legs) aggregated
35
+
36
+
29
37
  update( kwargs ) unless kwargs.empty?
30
38
  end
31
39
 
32
40
  def update( **kwargs )
33
41
  ## note: check with has_key? because value might be nil!!!
34
42
  @date = kwargs[:date] if kwargs.has_key? :date
43
+ @time = kwargs[:time] if kwargs.has_key? :time
35
44
 
36
45
  ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
37
46
  @team1 = kwargs[:team1] if kwargs.has_key? :team1
@@ -114,15 +123,17 @@ class Match
114
123
  ##
115
124
 
116
125
  ## for now score1 and score2 must be present
117
- if @score1.nil? || @score2.nil?
118
- puts "** WARN: missing scores for match:"
119
- pp kwargs
120
- ## exit 1
121
- end
126
+ ## if @score1.nil? || @score2.nil?
127
+ ## puts "** WARN: missing scores for match:"
128
+ ## pp kwargs
129
+ ## ## exit 1
130
+ ## end
122
131
 
123
132
  ## todo/fix: auto-calculate winner
124
133
  # return 1,2,0 1 => team1, 2 => team2, 0 => draw/tie
125
134
  ### calculate winner - use 1,2,0
135
+ ##
136
+ ## move winner calc to score class - why? why not?
126
137
  if @score1 && @score2
127
138
  if @score1 > @score2
128
139
  @winner = 1
@@ -145,13 +156,23 @@ class Match
145
156
  def complete?() true; end ## for now all scores are complete - in the future check scores; might be missing - not yet entered
146
157
 
147
158
 
148
- def score_str # pretty print (full time) scores; convenience method
149
- "#{@score1}-#{@score2}"
159
+ def score
160
+ Score.new( @score1i, @score2i, ## half time (first (i) part)
161
+ @score1, @score2, ## full time
162
+ @score1et, @score2et, ## extra time
163
+ @score1p, @score2p ) ## penalty
150
164
  end
151
165
 
152
- def scorei_str # pretty print (half time) scores; convenience method
153
- "#{@score1i}-#{@score2i}"
154
- end
166
+
167
+ ####
168
+ ## deprecated - use score.to_s and friends - why? why not?
169
+ # def score_str # pretty print (full time) scores; convenience method
170
+ # "#{@score1}-#{@score2}"
171
+ # end
172
+
173
+ # def scorei_str # pretty print (half time) scores; convenience method
174
+ # "#{@score1i}-#{@score2i}"
175
+ # end
155
176
  end # class Match
156
177
 
157
178
  end # module Sports
@@ -82,13 +82,13 @@ private
82
82
 
83
83
  def has_dates?() @start_date && @end_date; end
84
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
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
92
 
93
93
  def days() end_date.jd - start_date.jd; end
94
94
 
@@ -1,6 +1,6 @@
1
1
  module Sports
2
2
 
3
- class Round
3
+ class Round
4
4
  attr_reader :name, :start_date, :end_date, :knockout
5
5
  attr_accessor :num # note: make read & writable - why? why not?
6
6
 
@@ -17,7 +17,7 @@ module Sports
17
17
  @knockout = knockout
18
18
  @auto = auto # auto-created (inline reference/header without proper definition before)
19
19
  end
20
- end # class Round
20
+ end # class Round
21
21
 
22
22
  end # module Sports
23
23
 
@@ -37,7 +37,7 @@ class Standings
37
37
  end # (nested) class StandingsLine
38
38
 
39
39
 
40
- def initialize( opts={} )
40
+ def initialize( match_or_matches=nil, opts={} )
41
41
  ## fix:
42
42
  # passing in e.g. pts for win (3? 2? etc.)
43
43
  # default to 3 for now
@@ -46,6 +46,9 @@ class Standings
46
46
  @pts_won = opts[:pts_won] || 3
47
47
 
48
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
49
52
  end
50
53
 
51
54
 
@@ -65,6 +68,10 @@ class Standings
65
68
  end
66
69
 
67
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
+
68
75
  def to_a
69
76
  ## return lines; sort and add rank
70
77
  ## note: will update rank!!!! (side effect)
@@ -184,7 +191,7 @@ private
184
191
  team1 = m.team1.is_a?( String ) ? m.team1 : m.team1.name
185
192
  team2 = m.team2.is_a?( String ) ? m.team2 : m.team2.name
186
193
 
187
- score = m.score_str
194
+ score = m.score.to_s
188
195
 
189
196
  ## puts " #{team1} - #{team2} #{score}"
190
197
 
@@ -66,7 +66,7 @@ class Team
66
66
  ##############################
67
67
  ## helper methods for import only??
68
68
  ## check for duplicates
69
- include NameHelper
69
+ include SportDb::NameHelper
70
70
 
71
71
  def duplicates?
72
72
  names = [name] + alt_names + alt_names_auto
@@ -1,8 +1,10 @@
1
1
 
2
- module Sports
2
+ module SportDb
3
+ module Module
4
+ module Sports
3
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
- MINOR = 0
5
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 0
6
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
9
 
8
10
  def self.version
@@ -16,4 +18,7 @@ module Sports
16
18
  def self.root
17
19
  File.expand_path( File.dirname(File.dirname(__FILE__)) )
18
20
  end
19
- end # module Sports
21
+
22
+ end # module Sports
23
+ end
24
+ end
@@ -8,8 +8,8 @@ require 'helper'
8
8
 
9
9
  class TestMatchStatusParser < MiniTest::Test
10
10
 
11
- Status = Sports::Status
12
- StatusParser = Sports::StatusParser
11
+ Status = SportDb::Status
12
+ StatusParser = SportDb::StatusParser
13
13
 
14
14
  def test_find
15
15
  [['awarded [cancelled] canceled [ddd]', Status::CANCELLED],
@@ -24,7 +24,11 @@ class TestMatchStatusParser < MiniTest::Test
24
24
  puts "line (after): >#{line}<"
25
25
  puts
26
26
 
27
- assert_equal status_exp, status
27
+ if status_exp.nil?
28
+ assert_nil status
29
+ else
30
+ assert_equal status_exp, status
31
+ end
28
32
  end
29
33
  end # method test_find
30
34
 
@@ -38,7 +42,13 @@ class TestMatchStatusParser < MiniTest::Test
38
42
  ].each do |rec|
39
43
  str = rec[0]
40
44
  status_exp = rec[1]
41
- assert_equal status_exp, StatusParser.parse( str )
45
+ status = StatusParser.parse( str )
46
+
47
+ if status_exp.nil? ## for "silencing" minitest warning - Use assert_nil if expecting nil
48
+ assert_nil status
49
+ else
50
+ assert_equal status_exp, status
51
+ end
42
52
  end
43
53
  end
44
54
 
@@ -8,7 +8,7 @@ require 'helper'
8
8
 
9
9
  class TestNameHelper < MiniTest::Test
10
10
 
11
- include Sports::NameHelper
11
+ include SportDb::NameHelper
12
12
 
13
13
 
14
14
  def test_strip_norm ## strip (remove) non-norm characters e.g. ()'- etc.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-17 00:00:00.000000000 Z
11
+ date: 2020-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: alphabets
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.0.1
47
+ version: 0.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.0.1
54
+ version: 0.1.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: csvreader
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +110,7 @@ files:
110
110
  - Rakefile
111
111
  - lib/sports.rb
112
112
  - lib/sports/config.rb
113
+ - lib/sports/goal_parser_csv.rb
113
114
  - lib/sports/match_parser_csv.rb
114
115
  - lib/sports/match_status_parser.rb
115
116
  - lib/sports/name_helper.rb