sportdb-formats 0.1.6 → 0.1.7
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 +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:
|