sportdb-formats 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -0
- data/Rakefile +1 -1
- data/lib/sportdb/formats/goals.rb +277 -0
- data/lib/sportdb/formats/scores.rb +241 -0
- data/lib/sportdb/formats/version.rb +1 -1
- data/lib/sportdb/formats.rb +4 -0
- data/test/test_goals.rb +113 -0
- data/test/test_scores.rb +93 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 421d04b4b20f49a37bb1a31161bb4ab7b5df3113
|
4
|
+
data.tar.gz: e533ef2bb8dbb3c1053416c98ede4c9d20c42546
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6256ca94b85f87865a44b4f892273cb4c2c9f17e7847e765cd3c73f408a005907181e202ec201498fd3214a603aed6af33b4ecbcf4e2af43cca851f7b1ac9a09
|
7
|
+
data.tar.gz: 750122be629a4b902356d845658cfcccbb0403e75e3f713e1ff4bd0da4aefb6b2355bc828aecb8e21d20addb5bfb9c1924df58830eb85e4743aa340c560924df
|
data/Manifest.txt
CHANGED
@@ -4,12 +4,16 @@ README.md
|
|
4
4
|
Rakefile
|
5
5
|
lib/sportdb/formats.rb
|
6
6
|
lib/sportdb/formats/datafile.rb
|
7
|
+
lib/sportdb/formats/goals.rb
|
7
8
|
lib/sportdb/formats/outline_reader.rb
|
9
|
+
lib/sportdb/formats/scores.rb
|
8
10
|
lib/sportdb/formats/season_utils.rb
|
9
11
|
lib/sportdb/formats/version.rb
|
10
12
|
test/helper.rb
|
11
13
|
test/test_csv_reader.rb
|
12
14
|
test/test_datafile.rb
|
13
15
|
test/test_datafile_match.rb
|
16
|
+
test/test_goals.rb
|
14
17
|
test/test_outline_reader.rb
|
18
|
+
test/test_scores.rb
|
15
19
|
test/test_season_utils.rb
|
data/Rakefile
CHANGED
@@ -0,0 +1,277 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
class GoalsPlayerStruct
|
8
|
+
##
|
9
|
+
# note: player with own goal (o.g) gets listed on other team
|
10
|
+
# (thus, player might have two entries if also scored for its own team)
|
11
|
+
#
|
12
|
+
attr_accessor :name
|
13
|
+
attr_accessor :minutes # ary of minutes e.g. 30', 45+2', 72'
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@minutes = []
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class GoalsMinuteStruct
|
22
|
+
attr_accessor :minute, :offset
|
23
|
+
attr_accessor :penalty, :owngoal # flags
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@offset = 0
|
27
|
+
@penalty = false
|
28
|
+
@owngoal = false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class GoalStruct
|
34
|
+
######
|
35
|
+
# flat struct for goals - one entry per goals
|
36
|
+
attr_accessor :name
|
37
|
+
attr_accessor :team # 1 or 2 ? check/todo: add team1 or team2 flag?
|
38
|
+
attr_accessor :minute, :offset
|
39
|
+
attr_accessor :penalty, :owngoal
|
40
|
+
attr_accessor :score1, :score2 # gets calculated
|
41
|
+
|
42
|
+
## add pos for sequence number? e.g. 1,2,3,4 (1st goald, 2nd goal, etc.) ???
|
43
|
+
|
44
|
+
|
45
|
+
def initialize( **kwargs ) ## add/allow quick and dirty quick init with keywords
|
46
|
+
if kwargs.empty?
|
47
|
+
# do nothing
|
48
|
+
else
|
49
|
+
kwargs.each do |key,value|
|
50
|
+
send( "#{key}=", value )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ==(o)
|
56
|
+
o.class == self.class && o.state == state
|
57
|
+
end
|
58
|
+
|
59
|
+
def state
|
60
|
+
[@name, @team, @minute, @offset, @penalty, @owngoal, @score1, @score2]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
# todo: find a better name? to avoid confusing w/ GoalsParser? use MatchGoalsParser or similar?
|
67
|
+
class GoalsFinder
|
68
|
+
include LogUtils::Logging
|
69
|
+
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
# nothing here for now
|
73
|
+
end
|
74
|
+
|
75
|
+
def find!( line, opts={} )
|
76
|
+
# remove end-of-line comments
|
77
|
+
line = line.sub( /#.*$/ ) do |_|
|
78
|
+
logger.debug " cutting off end of line comment - >>#{$&}<<"
|
79
|
+
''
|
80
|
+
end
|
81
|
+
|
82
|
+
# remove [] if presents e.g. [Neymar 12']
|
83
|
+
line = line.gsub( /[\[\]]/, '' )
|
84
|
+
# remove (single match) if line starts w/ - (allow spaces) e.g. [-;Neymar 12'] or [ - ;Neymar 12']
|
85
|
+
line = line.sub( /^[ ]*-[ ]*/, '' )
|
86
|
+
|
87
|
+
# split into left hand side (lhs) for team1 and
|
88
|
+
# right hand side (rhs) for team2
|
89
|
+
|
90
|
+
values = line.split( ';' )
|
91
|
+
|
92
|
+
# note: allow empty right hand side (e.g. team2 did NOT score any goals e.g. 3-0 etc.)
|
93
|
+
lhs = values[0]
|
94
|
+
rhs = values[1]
|
95
|
+
|
96
|
+
lhs = lhs.strip unless lhs.nil?
|
97
|
+
rhs = rhs.strip unless rhs.nil?
|
98
|
+
|
99
|
+
parser = GoalsParser.new
|
100
|
+
## todo/check: only call if not nil?
|
101
|
+
|
102
|
+
logger.debug " lhs (team1): >#{lhs}<"
|
103
|
+
lhs_data = parser.parse!( lhs )
|
104
|
+
pp lhs_data
|
105
|
+
|
106
|
+
logger.debug " rhs (team2): >#{rhs}<"
|
107
|
+
rhs_data = parser.parse!( rhs )
|
108
|
+
pp rhs_data
|
109
|
+
|
110
|
+
### merge into flat goal structs
|
111
|
+
goals = []
|
112
|
+
lhs_data.each do |player|
|
113
|
+
player.minutes.each do |minute|
|
114
|
+
goal = GoalStruct.new
|
115
|
+
goal.name = player.name
|
116
|
+
goal.team = 1
|
117
|
+
goal.minute = minute.minute
|
118
|
+
goal.offset = minute.offset
|
119
|
+
goal.penalty = minute.penalty
|
120
|
+
goal.owngoal = minute.owngoal
|
121
|
+
goals << goal
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
rhs_data.each do |player|
|
126
|
+
player.minutes.each do |minute|
|
127
|
+
goal = GoalStruct.new
|
128
|
+
goal.name = player.name
|
129
|
+
goal.team = 2
|
130
|
+
goal.minute = minute.minute
|
131
|
+
goal.offset = minute.offset
|
132
|
+
goal.penalty = minute.penalty
|
133
|
+
goal.owngoal = minute.owngoal
|
134
|
+
goals << goal
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# sort by minute + offset
|
140
|
+
goals = goals.sort do |l,r|
|
141
|
+
res = l.minute <=> r.minute
|
142
|
+
if res == 0
|
143
|
+
res = l.offset <=> r.offset # pass 2: sort by offset
|
144
|
+
end
|
145
|
+
res
|
146
|
+
end
|
147
|
+
|
148
|
+
## calc score1,score2
|
149
|
+
score1 = 0
|
150
|
+
score2 = 0
|
151
|
+
goals.each do |goal|
|
152
|
+
if goal.team == 1
|
153
|
+
score1 += 1
|
154
|
+
elsif goal.team == 2
|
155
|
+
score2 += 1
|
156
|
+
else
|
157
|
+
# todo: should not happen: issue warning
|
158
|
+
end
|
159
|
+
goal.score1 = score1
|
160
|
+
goal.score2 = score2
|
161
|
+
end
|
162
|
+
|
163
|
+
logger.debug " #{goals.size} goals:"
|
164
|
+
pp goals
|
165
|
+
|
166
|
+
goals
|
167
|
+
end
|
168
|
+
|
169
|
+
end # class GoalsFinder
|
170
|
+
|
171
|
+
|
172
|
+
class GoalsParser
|
173
|
+
include LogUtils::Logging
|
174
|
+
|
175
|
+
|
176
|
+
# note: use ^ for start of string only!!!
|
177
|
+
# - for now slurp everything up to digits (inlc. spaces - use strip to remove)
|
178
|
+
# todo/check: use/rename to NAME_UNTIL_REGEX ??? ( add lookahead for spaces?)
|
179
|
+
NAME_REGEX = /^
|
180
|
+
[^0-9]+
|
181
|
+
/x
|
182
|
+
|
183
|
+
|
184
|
+
# todo/check: change to MINUTE_REGEX ??
|
185
|
+
# add MINUTE_SKIP_REGEX or MINUTE_SEP_REGEX /^[ ,]+/
|
186
|
+
# todo/fix: split out penalty and owngoal flag in PATTERN constant for reuse
|
187
|
+
MINUTES_REGEX = /^ # note: use ^ for start of string only!!!
|
188
|
+
(?<minute>[0-9]{1,3})
|
189
|
+
(?:\+
|
190
|
+
(?<offset>[1-9]{1})
|
191
|
+
)?
|
192
|
+
'
|
193
|
+
(?:[ ]*
|
194
|
+
\(
|
195
|
+
(?<type>P|pen\.|o\.g\.)
|
196
|
+
\)
|
197
|
+
)?
|
198
|
+
/x
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
def initialize
|
203
|
+
# nothing here for now
|
204
|
+
end
|
205
|
+
|
206
|
+
def parse!( line, opts={} )
|
207
|
+
|
208
|
+
## for now assume
|
209
|
+
## everything up-to 0-9 and , and () is part of player name
|
210
|
+
|
211
|
+
## try parsing lhs
|
212
|
+
## todo: check for empty - remove (make it same as empty string)
|
213
|
+
|
214
|
+
players = []
|
215
|
+
|
216
|
+
name = get_player_name!( line )
|
217
|
+
while name
|
218
|
+
logger.debug " found player name >#{name}< - remaining >#{line}<"
|
219
|
+
|
220
|
+
player = GoalsPlayerStruct.new
|
221
|
+
player.name = name
|
222
|
+
|
223
|
+
minute_hash = get_minute_hash!( line )
|
224
|
+
while minute_hash
|
225
|
+
logger.debug " found minutes >#{minute_hash.inspect}< - remaining >#{line}<"
|
226
|
+
|
227
|
+
minute = GoalsMinuteStruct.new
|
228
|
+
minute.minute = minute_hash[:minute].to_i
|
229
|
+
minute.offset = minute_hash[:offset].to_i if minute_hash[:offset]
|
230
|
+
if minute_hash[:type]
|
231
|
+
minute.owngoal = true if minute_hash[:type] =~ /o\.g\./
|
232
|
+
minute.penalty = true if minute_hash[:type] =~ /P|pen\./
|
233
|
+
end
|
234
|
+
player.minutes << minute
|
235
|
+
|
236
|
+
# remove commas and spaces (note: use ^ for start of string only!!!)
|
237
|
+
line.sub!( /^[ ,]+/, '' )
|
238
|
+
minute_hash = get_minute_hash!( line )
|
239
|
+
end
|
240
|
+
|
241
|
+
players << player
|
242
|
+
name = get_player_name!( line )
|
243
|
+
end
|
244
|
+
|
245
|
+
players
|
246
|
+
end # method parse!
|
247
|
+
|
248
|
+
private
|
249
|
+
def get_player_name!( line )
|
250
|
+
m = NAME_REGEX.match( line )
|
251
|
+
if m
|
252
|
+
## remove from line
|
253
|
+
line.slice!( 0...m[0].length )
|
254
|
+
m[0].strip # remove leading and trailing spaces
|
255
|
+
else
|
256
|
+
nil
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_minute_hash!( line )
|
261
|
+
m = MINUTES_REGEX.match( line ) # note: use ^ for start of string only!!!
|
262
|
+
if m
|
263
|
+
h = {}
|
264
|
+
# - note: do NOT forget to turn name into symbol for lookup in new hash (name.to_sym)
|
265
|
+
m.names.each { |n| h[n.to_sym] = m[n] } # or use match_data.names.zip( match_data.captures ) - more cryptic but "elegant"??
|
266
|
+
|
267
|
+
## remove matched string from line
|
268
|
+
line.slice!( 0...m[0].length )
|
269
|
+
h
|
270
|
+
else
|
271
|
+
nil
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
end # class GoalsParser
|
276
|
+
|
277
|
+
end # module SportDb
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
|
5
|
+
class ScoresFinder
|
6
|
+
|
7
|
+
include LogUtils::Logging
|
8
|
+
|
9
|
+
|
10
|
+
## e.g. 3-4 pen. 2-2 a.e.t. (1-1, 1-1)
|
11
|
+
EN__P_ET_FT_HT__REGEX = /\b
|
12
|
+
(?<score1p>\d{1,2})
|
13
|
+
-
|
14
|
+
(?<score2p>\d{1,2})
|
15
|
+
\s* # allow optional spaces
|
16
|
+
(?:p|pen\.?|pso) # e.g. pen, pen., PSO, p etc.
|
17
|
+
\s* # allow optional spaces
|
18
|
+
(?<score1et>\d{1,2})
|
19
|
+
-
|
20
|
+
(?<score2et>\d{1,2})
|
21
|
+
\s* # allow optional spaces
|
22
|
+
(?:aet|a\.e\.t\.)
|
23
|
+
\s* # allow optional spaces
|
24
|
+
\(
|
25
|
+
(?<score1>\d{1,2})
|
26
|
+
-
|
27
|
+
(?<score2>\d{1,2})
|
28
|
+
\s*
|
29
|
+
,
|
30
|
+
\s*
|
31
|
+
(?<score1i>\d{1,2})
|
32
|
+
-
|
33
|
+
(?<score2i>\d{1,2})
|
34
|
+
\)
|
35
|
+
(?=[\s\]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
|
36
|
+
## note: \b works only after non-alphanum e.g. )
|
37
|
+
|
38
|
+
|
39
|
+
## e.g. 2-1 a.e.t. (1-1, 0-0)
|
40
|
+
EN__ET_FT_HT__REGEX = /\b
|
41
|
+
(?<score1et>\d{1,2})
|
42
|
+
-
|
43
|
+
(?<score2et>\d{1,2})
|
44
|
+
\s* # allow optional spaces
|
45
|
+
(?:aet|a\.e\.t\.)
|
46
|
+
\s* # allow optional spaces
|
47
|
+
\(
|
48
|
+
(?<score1>\d{1,2})
|
49
|
+
-
|
50
|
+
(?<score2>\d{1,2})
|
51
|
+
\s*
|
52
|
+
,
|
53
|
+
\s*
|
54
|
+
(?<score1i>\d{1,2})
|
55
|
+
-
|
56
|
+
(?<score2i>\d{1,2})
|
57
|
+
\)
|
58
|
+
(?=[\s\]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
|
59
|
+
## note: \b works only after non-alphanum e.g. )
|
60
|
+
|
61
|
+
|
62
|
+
## e.g. 2-1 (1-1)
|
63
|
+
EN__FT_HT__REGEX = /\b
|
64
|
+
(?<score1>\d{1,2})
|
65
|
+
-
|
66
|
+
(?<score2>\d{1,2})
|
67
|
+
\s*
|
68
|
+
\(
|
69
|
+
(?<score1i>\d{1,2})
|
70
|
+
-
|
71
|
+
(?<score2i>\d{1,2})
|
72
|
+
\)
|
73
|
+
(?=[\s\]]|$)/x ## todo/check: remove loakahead assertion here - why require space?
|
74
|
+
## note: \b works only after non-alphanum e.g. )
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
###################
|
79
|
+
# more
|
80
|
+
|
81
|
+
# e.g. 1:2 or 0:2 or 3:3 or
|
82
|
+
# 1-1 or 0-2 or 3-3 or
|
83
|
+
# 1x1 or 1X1 or 0x2 or 3x3 -- used in Brazil / Portugal
|
84
|
+
FT_REGEX = /\b
|
85
|
+
(?<score1>\d{1,2})
|
86
|
+
[:\-xX]
|
87
|
+
(?<score2>\d{1,2})
|
88
|
+
\b/x
|
89
|
+
|
90
|
+
|
91
|
+
# e.g. 1:2nV => after extra time a.e.t
|
92
|
+
|
93
|
+
# note: possible ending w/ . -> thus cannot use /b will not work w/ .; use zero look-ahead
|
94
|
+
ET_REGEX = /\b
|
95
|
+
(?<score1>\d{1,2})
|
96
|
+
[:\-xX]
|
97
|
+
(?<score2>\d{1,2})
|
98
|
+
\s? # allow optional space
|
99
|
+
(?:nv|n\.v\.|aet|a\.e\.t\.) # allow optional . e.g. nV or n.V.
|
100
|
+
(?=[\s\)\]]|$)/xi
|
101
|
+
|
102
|
+
## todo: add/allow english markers e.g. pen or p ??
|
103
|
+
|
104
|
+
# e.g. 5:4iE => penalty / after penalty a.p
|
105
|
+
|
106
|
+
|
107
|
+
# note: possible ending w/ . -> thus cannot use /b will not work w/ .; use zero look-ahead
|
108
|
+
P_REGEX = /\b
|
109
|
+
(?<score1>\d{1,2})
|
110
|
+
[:\-xX]
|
111
|
+
(?<score2>\d{1,2})
|
112
|
+
\s? # allow optional space
|
113
|
+
(?:iE|i\.E\.|p|pen|PSO) # allow optional . e.g. iE or i.E.
|
114
|
+
(?=[\s\)\]]|$)/xi
|
115
|
+
|
116
|
+
|
117
|
+
## todo: allow all-in-one "literal form a la kicker" e.g.
|
118
|
+
# 2:2 (1:1, 1:0) n.V. 5:1 i.E.
|
119
|
+
|
120
|
+
def initialize
|
121
|
+
# nothing here for now
|
122
|
+
end
|
123
|
+
|
124
|
+
def find!( line, opts={} )
|
125
|
+
|
126
|
+
### fix: add and match all-in-one literal first, followed by
|
127
|
+
|
128
|
+
# note: always call after find_dates !!!
|
129
|
+
# scores match date-like patterns!! e.g. 10-11 or 10:00 etc.
|
130
|
+
# -- note: score might have two digits too
|
131
|
+
|
132
|
+
### fix: depending on language allow 1:1 or 1-1
|
133
|
+
## do NOT allow mix and match
|
134
|
+
## e.g. default to en is 1-1
|
135
|
+
## de is 1:1 etc.
|
136
|
+
|
137
|
+
|
138
|
+
# extract score from line
|
139
|
+
# and return it
|
140
|
+
# note: side effect - removes date from line string
|
141
|
+
|
142
|
+
|
143
|
+
score1i = nil # half time (ht) scores
|
144
|
+
score2i = nil
|
145
|
+
|
146
|
+
score1 = nil # full time (ft) scores
|
147
|
+
score2 = nil
|
148
|
+
|
149
|
+
score1et = nil # extra time (et) scores
|
150
|
+
score2et = nil
|
151
|
+
|
152
|
+
score1p = nil # penalty (p) scores
|
153
|
+
score2p = nil
|
154
|
+
|
155
|
+
|
156
|
+
if (md = EN__P_ET_FT_HT__REGEX.match( line ))
|
157
|
+
score1i = md[:score1i].to_i
|
158
|
+
score2i = md[:score2i].to_i
|
159
|
+
score1 = md[:score1].to_i
|
160
|
+
score2 = md[:score2].to_i
|
161
|
+
score1et = md[:score1et].to_i
|
162
|
+
score2et = md[:score2et].to_i
|
163
|
+
score1p = md[:score1p].to_i
|
164
|
+
score2p = md[:score2p].to_i
|
165
|
+
|
166
|
+
logger.debug " score.en__p_et_ft_ht: >#{score1p}-#{score2p} pen. #{score1et}-#{score2et} a.e.t. (#{score1}-#{score2}, #{score1i}-#{score2i})<"
|
167
|
+
|
168
|
+
line.sub!( md[0], '[SCORES.EN__P_ET_FT_HT]' )
|
169
|
+
|
170
|
+
elsif (md = EN__ET_FT_HT__REGEX.match( line ))
|
171
|
+
score1i = md[:score1i].to_i
|
172
|
+
score2i = md[:score2i].to_i
|
173
|
+
score1 = md[:score1].to_i
|
174
|
+
score2 = md[:score2].to_i
|
175
|
+
score1et = md[:score1et].to_i
|
176
|
+
score2et = md[:score2et].to_i
|
177
|
+
|
178
|
+
logger.debug " score.en__et_ft_ht: >#{score1et}-#{score2et} a.e.t. (#{score1}-#{score2}, #{score1i}-#{score2i})<"
|
179
|
+
|
180
|
+
line.sub!( md[0], '[SCORES.EN__ET_FT_HT]' )
|
181
|
+
|
182
|
+
elsif (md = EN__FT_HT__REGEX.match( line ))
|
183
|
+
score1i = md[:score1i].to_i
|
184
|
+
score2i = md[:score2i].to_i
|
185
|
+
score1 = md[:score1].to_i
|
186
|
+
score2 = md[:score2].to_i
|
187
|
+
|
188
|
+
logger.debug " score.en__ft_ht: >#{score1}-#{score2} (#{score1i}-#{score2i})<"
|
189
|
+
|
190
|
+
line.sub!( md[0], '[SCORES.EN__FT_HT]' )
|
191
|
+
else
|
192
|
+
#######################################################
|
193
|
+
## try "standard" generic patterns for fallbacks
|
194
|
+
|
195
|
+
if (md = ET_REGEX.match( line ))
|
196
|
+
score1et = md[:score1].to_i
|
197
|
+
score2et = md[:score2].to_i
|
198
|
+
|
199
|
+
logger.debug " score.et: >#{score1et}-#{score2et}<"
|
200
|
+
|
201
|
+
line.sub!( md[0], '[SCORE.ET]' )
|
202
|
+
end
|
203
|
+
|
204
|
+
if (md = P_REGEX.match( line ))
|
205
|
+
score1p = md[:score1].to_i
|
206
|
+
score2p = md[:score2].to_i
|
207
|
+
|
208
|
+
logger.debug " score.p: >#{score1p}-#{score2p}<"
|
209
|
+
|
210
|
+
line.sub!( md[0], '[SCORE.P]' )
|
211
|
+
end
|
212
|
+
|
213
|
+
## let full time (ft) standard regex go last - has no marker
|
214
|
+
|
215
|
+
if (md = FT_REGEX.match( line ))
|
216
|
+
score1 = md[:score1].to_i
|
217
|
+
score2 = md[:score2].to_i
|
218
|
+
|
219
|
+
logger.debug " score: >#{score1}-#{score2}<"
|
220
|
+
|
221
|
+
line.sub!( md[0], '[SCORE]' )
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
## todo: how to handle game w/o extra time
|
226
|
+
# but w/ optional penalty ??? e.g. used in copa liberatores, for example
|
227
|
+
# retrun 0,0 or nil,nil for extra time score ?? or -1, -1 ??
|
228
|
+
# for now use nil,nil
|
229
|
+
|
230
|
+
scores = []
|
231
|
+
scores += [score1i, score2i] if score1p || score2p || score1et || score2et || score1 || score2 || score1i || score2i
|
232
|
+
scores += [score1, score2] if score1p || score2p || score1et || score2et || score1 || score2
|
233
|
+
scores += [score1et, score2et] if score1p || score2p || score1et || score2et
|
234
|
+
scores += [score1p, score2p] if score1p || score2p
|
235
|
+
|
236
|
+
scores
|
237
|
+
end
|
238
|
+
|
239
|
+
end # class ScoresFinder
|
240
|
+
|
241
|
+
end # module SportDb
|
data/lib/sportdb/formats.rb
CHANGED
@@ -27,6 +27,10 @@ require 'sportdb/formats/datafile'
|
|
27
27
|
require 'sportdb/formats/season_utils'
|
28
28
|
|
29
29
|
|
30
|
+
require 'sportdb/formats/scores'
|
31
|
+
require 'sportdb/formats/goals'
|
32
|
+
|
33
|
+
|
30
34
|
## let's put test configuration in its own namespace / module
|
31
35
|
module SportDb
|
32
36
|
class Test ## todo/check: works with module too? use a module - why? why not?
|
data/test/test_goals.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_goals.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestGoals < MiniTest::Test
|
11
|
+
|
12
|
+
|
13
|
+
def test_world_cup_1930
|
14
|
+
line = " [L. Laurent 19' Langiller 40' Maschinot 43', 87'; Carreño 70']"
|
15
|
+
|
16
|
+
assert_equal [
|
17
|
+
SportDb::GoalStruct.new(
|
18
|
+
name: 'L. Laurent',
|
19
|
+
minute: 19,
|
20
|
+
offset: 0,
|
21
|
+
owngoal: false,
|
22
|
+
penalty: false,
|
23
|
+
score1: 1,
|
24
|
+
score2: 0,
|
25
|
+
team: 1),
|
26
|
+
SportDb::GoalStruct.new(
|
27
|
+
name: 'Langiller',
|
28
|
+
minute: 40,
|
29
|
+
offset: 0,
|
30
|
+
owngoal: false,
|
31
|
+
penalty: false,
|
32
|
+
score1: 2,
|
33
|
+
score2: 0,
|
34
|
+
team: 1),
|
35
|
+
SportDb::GoalStruct.new(
|
36
|
+
name: 'Maschinot',
|
37
|
+
minute: 43,
|
38
|
+
offset: 0,
|
39
|
+
owngoal: false,
|
40
|
+
penalty: false,
|
41
|
+
score1: 3,
|
42
|
+
score2: 0,
|
43
|
+
team: 1),
|
44
|
+
SportDb::GoalStruct.new(
|
45
|
+
name: 'Carreño',
|
46
|
+
minute: 70,
|
47
|
+
offset: 0,
|
48
|
+
owngoal: false,
|
49
|
+
penalty: false,
|
50
|
+
score1: 3,
|
51
|
+
score2: 1,
|
52
|
+
team: 2),
|
53
|
+
SportDb::GoalStruct.new(
|
54
|
+
name: 'Maschinot',
|
55
|
+
minute: 87,
|
56
|
+
offset: 0,
|
57
|
+
owngoal: false,
|
58
|
+
penalty: false,
|
59
|
+
score1: 4,
|
60
|
+
score2: 1,
|
61
|
+
team: 1)], parse_goals( line )
|
62
|
+
|
63
|
+
|
64
|
+
line = " [Monti 81'] "
|
65
|
+
assert_equal [
|
66
|
+
SportDb::GoalStruct.new(
|
67
|
+
name: 'Monti',
|
68
|
+
minute: 81,
|
69
|
+
offset: 0,
|
70
|
+
owngoal: false,
|
71
|
+
penalty: false,
|
72
|
+
score1: 1,
|
73
|
+
score2: 0,
|
74
|
+
team: 1)], parse_goals( line )
|
75
|
+
|
76
|
+
|
77
|
+
line = " [Vidal 3', 65' M. Rosas 51' (o.g.)]"
|
78
|
+
assert_equal [
|
79
|
+
SportDb::GoalStruct.new(
|
80
|
+
name: 'Vidal',
|
81
|
+
minute: 3,
|
82
|
+
offset: 0,
|
83
|
+
owngoal: false,
|
84
|
+
penalty: false,
|
85
|
+
score1: 1,
|
86
|
+
score2: 0,
|
87
|
+
team: 1),
|
88
|
+
SportDb::GoalStruct.new(
|
89
|
+
name: 'M. Rosas',
|
90
|
+
minute: 51,
|
91
|
+
offset: 0,
|
92
|
+
owngoal: true,
|
93
|
+
penalty: false,
|
94
|
+
score1: 2,
|
95
|
+
score2: 0,
|
96
|
+
team: 1),
|
97
|
+
SportDb::GoalStruct.new(
|
98
|
+
name: 'Vidal',
|
99
|
+
minute: 65,
|
100
|
+
offset: 0,
|
101
|
+
owngoal: false,
|
102
|
+
penalty: false,
|
103
|
+
score1: 3,
|
104
|
+
score2: 0,
|
105
|
+
team: 1)], parse_goals( line )
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def parse_goals( line )
|
110
|
+
SportDb::GoalsFinder.new.find!( line )
|
111
|
+
end
|
112
|
+
|
113
|
+
end # class TestGoals
|
data/test/test_scores.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_scores.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestScores < MiniTest::Test
|
11
|
+
|
12
|
+
|
13
|
+
def test_scores
|
14
|
+
data = [
|
15
|
+
[ '10:0', [nil,nil,10,0]],
|
16
|
+
[ '1:22', [nil,nil,1,22]],
|
17
|
+
[ '1-22', [nil,nil,1,22]],
|
18
|
+
[ '1x22', [nil,nil,1,22]],
|
19
|
+
[ '1X22', [nil,nil,1,22]],
|
20
|
+
|
21
|
+
|
22
|
+
## do not support three digits
|
23
|
+
[ '1-222', []],
|
24
|
+
[ '111-0', []],
|
25
|
+
[ '1:222', []],
|
26
|
+
[ '111:0', []],
|
27
|
+
[ '111x0', []],
|
28
|
+
[ '111X0', []],
|
29
|
+
|
30
|
+
## penality only
|
31
|
+
[ '3-4iE', [nil,nil,nil,nil,nil,nil,3,4]],
|
32
|
+
[ '3:4iE', [nil,nil,nil,nil,nil,nil,3,4]],
|
33
|
+
[ '3:4 iE', [nil,nil,nil,nil,nil,nil,3,4]],
|
34
|
+
[ '3:4 i.E.', [nil,nil,nil,nil,nil,nil,3,4]],
|
35
|
+
[ '3-4 pen', [nil,nil,nil,nil,nil,nil,3,4]],
|
36
|
+
[ '3-4 PSO', [nil,nil,nil,nil,nil,nil,3,4]], # PSO => penalty shotout
|
37
|
+
[ '3-4p', [nil,nil,nil,nil,nil,nil,3,4]],
|
38
|
+
[ '3-4 p', [nil,nil,nil,nil,nil,nil,3,4]],
|
39
|
+
|
40
|
+
## extra time only - allow ?? why not ?? only allow penalty w/ missing extra time?
|
41
|
+
## todo/fix: issue warning or error in parser!!!
|
42
|
+
[ '3-4nV', [nil,nil,nil,nil,3,4]],
|
43
|
+
[ '3:4nV', [nil,nil,nil,nil,3,4]],
|
44
|
+
[ '3-4 aet', [nil,nil,nil,nil,3,4]],
|
45
|
+
[ '3-4 a.e.t.', [nil,nil,nil,nil,3,4]],
|
46
|
+
|
47
|
+
[ '3:4nV 1:1', [nil,nil,1,1,3,4]],
|
48
|
+
[ '1:1 3:4nV', [nil,nil,1,1,3,4]],
|
49
|
+
[ '3:4 nV 1:1', [nil,nil,1,1,3,4]],
|
50
|
+
[ '3:4 n.V. 1:1', [nil,nil,1,1,3,4]],
|
51
|
+
|
52
|
+
[ '3:4iE 1:1', [nil,nil,1,1,nil,nil,3,4]],
|
53
|
+
[ '1:1 3:4iE', [nil,nil,1,1,nil,nil,3,4]],
|
54
|
+
|
55
|
+
[ '1:1 2:2nV 3:4iE', [nil,nil,1,1,2,2,3,4]],
|
56
|
+
[ '3:4iE 2:2nV 1:1', [nil,nil,1,1,2,2,3,4]],
|
57
|
+
[ '3:4 i.E. 2:2 n.V. 1:1', [nil,nil,1,1,2,2,3,4]],
|
58
|
+
[ '3-4p 2-2aet 1-1', [nil,nil,1,1,2,2,3,4]],
|
59
|
+
[ '3-4 pen 2-2 aet 1-1', [nil,nil,1,1,2,2,3,4]],
|
60
|
+
|
61
|
+
#####################################################
|
62
|
+
## check new all-in-one english (en) formats / patterns
|
63
|
+
[ '2-1 (1-1)', [1,1,2,1]],
|
64
|
+
[ '2-1 a.e.t. (1-1, 0-0)', [0,0,1,1,2,1]],
|
65
|
+
[ '2-1aet (1-1, 0-0)', [0,0,1,1,2,1]],
|
66
|
+
[ '2-1 A.E.T. (1-1, 0-0)', [0,0,1,1,2,1]],
|
67
|
+
[ '2-1AET (1-1, 0-0)', [0,0,1,1,2,1]],
|
68
|
+
[ '3-4 pen. 2-2 a.e.t. (1-1, 1-1)', [1,1,1,1,2,2,3,4]],
|
69
|
+
[ '3-4 pen 2-2 a.e.t. (1-1, 1-1)', [1,1,1,1,2,2,3,4]],
|
70
|
+
[ '3-4 pen 2-2 a.e.t. (1-1, 1-1)', [1,1,1,1,2,2,3,4]],
|
71
|
+
[ '3-4p 2-2aet (1-1, 1-1)', [1,1,1,1,2,2,3,4]],
|
72
|
+
[ '3-4PSO 2-2AET (1-1, 1-1)', [1,1,1,1,2,2,3,4]],
|
73
|
+
]
|
74
|
+
|
75
|
+
assert_scores( data )
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def assert_scores( data )
|
80
|
+
data.each do |rec|
|
81
|
+
line = rec[0]
|
82
|
+
exp = rec[1]
|
83
|
+
|
84
|
+
assert_equal exp, parse_scores( line )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_scores( line )
|
89
|
+
finder = SportDb::ScoresFinder.new
|
90
|
+
finder.find!( line )
|
91
|
+
end
|
92
|
+
|
93
|
+
end # class TestScores
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sportdb-formats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: alphabets
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.2.
|
33
|
+
version: 0.2.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.2.
|
40
|
+
version: 0.2.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: csvreader
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,14 +109,18 @@ files:
|
|
109
109
|
- Rakefile
|
110
110
|
- lib/sportdb/formats.rb
|
111
111
|
- lib/sportdb/formats/datafile.rb
|
112
|
+
- lib/sportdb/formats/goals.rb
|
112
113
|
- lib/sportdb/formats/outline_reader.rb
|
114
|
+
- lib/sportdb/formats/scores.rb
|
113
115
|
- lib/sportdb/formats/season_utils.rb
|
114
116
|
- lib/sportdb/formats/version.rb
|
115
117
|
- test/helper.rb
|
116
118
|
- test/test_csv_reader.rb
|
117
119
|
- test/test_datafile.rb
|
118
120
|
- test/test_datafile_match.rb
|
121
|
+
- test/test_goals.rb
|
119
122
|
- test/test_outline_reader.rb
|
123
|
+
- test/test_scores.rb
|
120
124
|
- test/test_season_utils.rb
|
121
125
|
homepage: https://github.com/sportdb/sport.db
|
122
126
|
licenses:
|