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 +4 -4
- data/Manifest.txt +1 -0
- data/README.md +280 -2
- data/Rakefile +2 -2
- data/lib/sports.rb +32 -5
- data/lib/sports/config.rb +27 -12
- data/lib/sports/goal_parser_csv.rb +28 -0
- data/lib/sports/match_parser_csv.rb +37 -3
- data/lib/sports/match_status_parser.rb +6 -3
- data/lib/sports/name_helper.rb +1 -1
- data/lib/sports/season.rb +1 -1
- data/lib/sports/structs/goal.rb +4 -4
- data/lib/sports/structs/match.rb +31 -10
- data/lib/sports/structs/matchlist.rb +7 -7
- data/lib/sports/structs/round.rb +2 -2
- data/lib/sports/structs/standings.rb +9 -2
- data/lib/sports/structs/team.rb +1 -1
- data/lib/sports/version.rb +9 -4
- data/test/test_match_status_parser.rb +14 -4
- data/test/test_name_helper.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 831dacee645acf5dea757570924ad7c9e1e2212f
|
4
|
+
data.tar.gz: 676b1114696241c5113854fc523821da2a8155ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61ad3469162b1c9269166ec3e4ed8fa739be3e181e7e0fa74bd3440ef631f046a293fe63752c4c28d9f5c4a856ddc83e6f15b94b347c3c50b773adb2b4b98768
|
7
|
+
data.tar.gz: 3cde3acf5f4a7756f0b68d5c4686f374f6cf45f967f4070c8b952e7747e0bf51ebb32d464a4e71b9e887dcac40b7b9ac12e1f6cf3aa1cdb8c270982d2ba73d75
|
data/Manifest.txt
CHANGED
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
|
-
|
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
|
25
|
+
['score-formats', '>= 0.1.0'],
|
26
26
|
['csvreader', '>= 1.2.4'],
|
27
27
|
]
|
28
28
|
|
data/lib/sports.rb
CHANGED
@@ -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
|
-
|
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
|
|
data/lib/sports/config.rb
CHANGED
@@ -1,25 +1,40 @@
|
|
1
1
|
|
2
|
-
module
|
3
|
-
|
4
|
-
class Configuration
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
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
|
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
|
-
|
87
|
+
end # class StatusParser
|
85
88
|
|
86
|
-
end # module
|
89
|
+
end # module SportDb
|
87
90
|
|
data/lib/sports/name_helper.rb
CHANGED
data/lib/sports/season.rb
CHANGED
@@ -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
|
-
|
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 ) &&
|
data/lib/sports/structs/goal.rb
CHANGED
@@ -47,8 +47,8 @@ class GoalEvent
|
|
47
47
|
)?
|
48
48
|
['.]
|
49
49
|
$}x.match( row['Minute'])
|
50
|
-
minute =
|
51
|
-
offset =
|
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
|
|
data/lib/sports/structs/match.rb
CHANGED
@@ -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
|
-
|
119
|
-
|
120
|
-
|
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
|
149
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
|
data/lib/sports/structs/round.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sports
|
2
2
|
|
3
|
-
|
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
|
-
|
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.
|
194
|
+
score = m.score.to_s
|
188
195
|
|
189
196
|
## puts " #{team1} - #{team2} #{score}"
|
190
197
|
|
data/lib/sports/structs/team.rb
CHANGED
data/lib/sports/version.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
|
2
|
-
module
|
2
|
+
module SportDb
|
3
|
+
module Module
|
4
|
+
module Sports
|
3
5
|
MAJOR = 0 ## todo: namespace inside version or something - why? why not??
|
4
|
-
MINOR =
|
5
|
-
PATCH =
|
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
|
-
|
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 =
|
12
|
-
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
|
-
|
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
|
-
|
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
|
|
data/test/test_name_helper.rb
CHANGED
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
|
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-
|
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
|
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
|
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
|