sports 0.0.1 → 0.1.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 +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
|