sportdb-quick 0.5.2 → 0.7.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/CHANGELOG.md +1 -1
- data/Manifest.txt +12 -3
- data/Rakefile +2 -2
- data/lib/sportdb/quick/match_parser.rb +12 -537
- data/lib/sportdb/quick/match_tree/goal.rb +67 -0
- data/lib/sportdb/quick/match_tree/group.rb +25 -0
- data/lib/sportdb/quick/match_tree/match.rb +247 -0
- data/lib/sportdb/quick/match_tree/round.rb +36 -0
- data/lib/sportdb/quick/match_tree-helpers.rb +81 -0
- data/lib/sportdb/quick/match_tree.rb +162 -0
- data/lib/sportdb/quick/match_tree_on/on_date_header.rb +45 -0
- data/lib/sportdb/quick/match_tree_on/on_goal_line.rb +87 -0
- data/lib/sportdb/quick/match_tree_on/on_group_def.rb +27 -0
- data/lib/sportdb/quick/match_tree_on/on_match_line.rb +195 -0
- data/lib/sportdb/quick/match_tree_on/on_round_def.rb +85 -0
- data/lib/sportdb/quick/match_tree_on/on_round_outline.rb +104 -0
- data/lib/sportdb/quick/quick_match_reader.rb +65 -68
- data/lib/sportdb/quick/version.rb +2 -2
- data/lib/sportdb/quick.rb +19 -16
- metadata +19 -10
- data/lib/sportdb/quick/outline.rb +0 -96
- data/lib/sportdb/quick/outline_reader.rb +0 -98
- data/lib/sportdb/quick/quick_league_outline.rb +0 -123
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b48a679900d31129e10a922ffa2bdea28bd919d6effe565e958d915cfc69a18
|
|
4
|
+
data.tar.gz: '089b0cf75426611e738012751e446cc8b3e319027a6ed4f6872fe1d773467bac'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 677234ed2602b3b153713c58d4a398c3e73a72d48c19f83667c4fa0764331c8971db60f1091800380c146a10041e281b48df8299ac1d493604f5f5dfc59aebf9
|
|
7
|
+
data.tar.gz: c66fb17fae6acf92fe4aa1177aa3758d8073b7e88f7e25de73942e275b8bf6652a1239fbcea7ee7221c87e87634b3f5d53c312edf97b637e358da3602b1494a7
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
|
@@ -4,8 +4,17 @@ README.md
|
|
|
4
4
|
Rakefile
|
|
5
5
|
lib/sportdb/quick.rb
|
|
6
6
|
lib/sportdb/quick/match_parser.rb
|
|
7
|
-
lib/sportdb/quick/
|
|
8
|
-
lib/sportdb/quick/
|
|
9
|
-
lib/sportdb/quick/
|
|
7
|
+
lib/sportdb/quick/match_tree-helpers.rb
|
|
8
|
+
lib/sportdb/quick/match_tree.rb
|
|
9
|
+
lib/sportdb/quick/match_tree/goal.rb
|
|
10
|
+
lib/sportdb/quick/match_tree/group.rb
|
|
11
|
+
lib/sportdb/quick/match_tree/match.rb
|
|
12
|
+
lib/sportdb/quick/match_tree/round.rb
|
|
13
|
+
lib/sportdb/quick/match_tree_on/on_date_header.rb
|
|
14
|
+
lib/sportdb/quick/match_tree_on/on_goal_line.rb
|
|
15
|
+
lib/sportdb/quick/match_tree_on/on_group_def.rb
|
|
16
|
+
lib/sportdb/quick/match_tree_on/on_match_line.rb
|
|
17
|
+
lib/sportdb/quick/match_tree_on/on_round_def.rb
|
|
18
|
+
lib/sportdb/quick/match_tree_on/on_round_outline.rb
|
|
10
19
|
lib/sportdb/quick/quick_match_reader.rb
|
|
11
20
|
lib/sportdb/quick/version.rb
|
data/Rakefile
CHANGED
|
@@ -21,8 +21,8 @@ Hoe.spec 'sportdb-quick' do
|
|
|
21
21
|
self.licenses = ['Public Domain']
|
|
22
22
|
|
|
23
23
|
self.extra_deps = [
|
|
24
|
-
['sportdb-parser', '>= 0.
|
|
25
|
-
['
|
|
24
|
+
['sportdb-parser', '>= 0.7.0'],
|
|
25
|
+
['season-formats', '>= 0.1.0'],
|
|
26
26
|
['logutils', '>= 0.6.1'],
|
|
27
27
|
]
|
|
28
28
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
module SportDb
|
|
3
2
|
|
|
4
3
|
class MatchParser ## simple match parser for team match schedules
|
|
@@ -9,74 +8,18 @@ class MatchParser ## simple match parser for team match schedules
|
|
|
9
8
|
|
|
10
9
|
include Logging ## e.g. logger#debug, logger#info, etc.
|
|
11
10
|
|
|
12
|
-
def log( msg )
|
|
13
|
-
## append msg to ./logs.txt
|
|
14
|
-
## use ./errors.txt - why? why not?
|
|
15
|
-
File.open( './logs.txt', 'a:utf-8' ) do |f|
|
|
16
|
-
f.write( msg )
|
|
17
|
-
f.write( "\n" )
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
|
|
22
11
|
|
|
23
12
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## check if lines_or_txt is an array or just a string
|
|
28
|
-
## use teams: like start: why? why not?
|
|
29
|
-
parser = new( lines, start )
|
|
13
|
+
def self.parse( txt, start: )
|
|
14
|
+
## todo/check - add teams: option like start: why? why not?
|
|
15
|
+
parser = new( txt, start: start )
|
|
30
16
|
parser.parse
|
|
31
17
|
end
|
|
32
18
|
|
|
19
|
+
def initialize( txt, start: )
|
|
20
|
+
@txt = txt
|
|
21
|
+
@start = start
|
|
33
22
|
|
|
34
|
-
|
|
35
|
-
def _prep_lines( lines ) ## todo/check: add alias preproc_lines or build_lines or prep_lines etc. - why? why not?
|
|
36
|
-
|
|
37
|
-
## todo/fix - rework and make simpler
|
|
38
|
-
## no need to double join array of string to txt etc.
|
|
39
|
-
|
|
40
|
-
txt = if lines.is_a?( Array )
|
|
41
|
-
## join together with newline
|
|
42
|
-
lines.reduce( String.new ) do |mem,line|
|
|
43
|
-
mem << line; mem << "\n"; mem
|
|
44
|
-
end
|
|
45
|
-
else ## assume single-all-in-one txt
|
|
46
|
-
lines
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
## preprocess automagically - why? why not?
|
|
50
|
-
## strip lines with comments and empty lines striped / removed
|
|
51
|
-
txt_new = String.new
|
|
52
|
-
txt.each_line do |line| ## preprocess
|
|
53
|
-
line = line.strip
|
|
54
|
-
next if line.empty? || line.start_with?('#') ### skip empty lines and comments
|
|
55
|
-
|
|
56
|
-
line = line.sub( /#.*/, '' ).strip ### cut-off end-of line comments too
|
|
57
|
-
|
|
58
|
-
txt_new << line
|
|
59
|
-
txt_new << "\n"
|
|
60
|
-
end
|
|
61
|
-
txt_new
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
#
|
|
66
|
-
# todo/fix: change start to start: too!!!
|
|
67
|
-
# might be optional in the future!! - why? why not?
|
|
68
|
-
|
|
69
|
-
def initialize( lines, start )
|
|
70
|
-
# for convenience split string into lines
|
|
71
|
-
## note: removes/strips empty lines
|
|
72
|
-
## todo/check: change to text instead of array of lines - why? why not?
|
|
73
|
-
|
|
74
|
-
## note - wrap in enumerator/iterator a.k.a lines reader
|
|
75
|
-
## @lines = lines.is_a?( String ) ?
|
|
76
|
-
## _read_lines( lines ) : lines
|
|
77
|
-
|
|
78
|
-
@txt = _prep_lines( lines )
|
|
79
|
-
@start = start
|
|
80
23
|
@errors = []
|
|
81
24
|
end
|
|
82
25
|
|
|
@@ -87,23 +30,7 @@ class MatchParser ## simple match parser for team match schedules
|
|
|
87
30
|
def parse
|
|
88
31
|
## note: every (new) read call - resets errors list to empty
|
|
89
32
|
@errors = []
|
|
90
|
-
|
|
91
|
-
@last_date = nil
|
|
92
|
-
@last_time = nil
|
|
93
|
-
@last_round = nil
|
|
94
|
-
@last_group = nil
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@teams = Hash.new(0) ## track counts (only) for now for (interal) team stats - why? why not?
|
|
98
|
-
@rounds = {}
|
|
99
|
-
@groups = {}
|
|
100
|
-
@matches = []
|
|
101
|
-
|
|
102
|
-
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@tree = []
|
|
106
|
-
|
|
33
|
+
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
|
107
34
|
|
|
108
35
|
if debug?
|
|
109
36
|
puts "lines:"
|
|
@@ -131,471 +58,19 @@ class MatchParser ## simple match parser for team match schedules
|
|
|
131
58
|
=end
|
|
132
59
|
|
|
133
60
|
parser = RaccMatchParser.new( @txt ) ## use own parser instance (not shared) - why? why not?
|
|
134
|
-
|
|
61
|
+
tree = parser.parse
|
|
135
62
|
## pp @tree
|
|
136
63
|
|
|
137
64
|
## report parse errors here - why? why not?
|
|
138
65
|
|
|
139
66
|
|
|
140
67
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if node.is_a? RaccMatchParser::RoundDef
|
|
144
|
-
## todo/fix: add round definition (w begin n end date)
|
|
145
|
-
## todo: do not patch rounds with definition (already assume begin/end date is good)
|
|
146
|
-
## -- how to deal with matches that get rescheduled/postponed?
|
|
147
|
-
on_round_def( node )
|
|
148
|
-
elsif node.is_a? RaccMatchParser::GroupDef ## NB: group goes after round (round may contain group marker too)
|
|
149
|
-
### todo: add pipe (|) marker (required)
|
|
150
|
-
on_group_def( node )
|
|
151
|
-
elsif node.is_a? RaccMatchParser::GroupHeader
|
|
152
|
-
on_group_header( node )
|
|
153
|
-
elsif node.is_a? RaccMatchParser::RoundHeader
|
|
154
|
-
on_round_header( node )
|
|
155
|
-
elsif node.is_a? RaccMatchParser::DateHeader
|
|
156
|
-
on_date_header( node )
|
|
157
|
-
elsif node.is_a? RaccMatchParser::MatchLine
|
|
158
|
-
on_match_line( node )
|
|
159
|
-
elsif node.is_a? RaccMatchParser::GoalLine
|
|
160
|
-
on_goal_line( node )
|
|
161
|
-
elsif node.is_a? RaccMatchParser::LineupLine
|
|
162
|
-
## skip lineup for now
|
|
163
|
-
else
|
|
164
|
-
## report error
|
|
165
|
-
msg = "!! WARN - unknown node (parse tree type) - #{node.class.name}"
|
|
166
|
-
puts msg
|
|
167
|
-
pp node
|
|
168
|
-
|
|
169
|
-
log( msg )
|
|
170
|
-
log( node.pretty_inspect )
|
|
171
|
-
end
|
|
172
|
-
end # tree.each
|
|
173
|
-
|
|
174
|
-
## note - team keys are names and values are "internal" stats!!
|
|
68
|
+
## note - team keys are names and values are "internal" stats e.g. usage count!!
|
|
175
69
|
## and NOT team/club/nat_team structs!!
|
|
176
|
-
[@teams.keys, @matches, @rounds.values, @groups.values]
|
|
70
|
+
## returns [@teams.keys, @matches, @rounds.values, @groups.values]
|
|
71
|
+
conv = MatchTree.new( tree, start: @start )
|
|
72
|
+
conv.convert
|
|
177
73
|
end # method parse
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def on_group_header( node )
|
|
182
|
-
logger.debug "on group header: >#{node}<"
|
|
183
|
-
|
|
184
|
-
# note: group header resets (last) round (allows, for example):
|
|
185
|
-
# e.g.
|
|
186
|
-
# Group Playoffs/Replays -- round header
|
|
187
|
-
# team1 team2 -- match
|
|
188
|
-
# Group B -- group header
|
|
189
|
-
# team1 team2 - match (will get new auto-matchday! not last round)
|
|
190
|
-
@last_round = nil
|
|
191
|
-
|
|
192
|
-
name = node.name
|
|
193
|
-
|
|
194
|
-
group = @groups[ name ]
|
|
195
|
-
if group.nil?
|
|
196
|
-
puts "!! PARSE ERROR - no group def found for >#{name}<"
|
|
197
|
-
exit 1
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# set group for games
|
|
201
|
-
@last_group = group
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def on_group_def( node )
|
|
206
|
-
logger.debug "on group def: >#{node}<"
|
|
207
|
-
|
|
208
|
-
## e.g
|
|
209
|
-
## [:group_def, "Group A"],
|
|
210
|
-
## [:team, "Germany"],
|
|
211
|
-
## [:team, "Scotland"],
|
|
212
|
-
## [:team, "Hungary"],
|
|
213
|
-
## [:team, "Switzerland"]
|
|
214
|
-
|
|
215
|
-
node.teams.each do |team|
|
|
216
|
-
@teams[ team ] += 1
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
## todo/check/fix: add back group key - why? why not?
|
|
220
|
-
group = Import::Group.new( name: node.name,
|
|
221
|
-
teams: node.teams )
|
|
222
|
-
|
|
223
|
-
@groups[ node.name ] = group
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def _build_date( m:, d:, y:, start: )
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
## quick debug hack
|
|
231
|
-
if m == 2 && d == 29
|
|
232
|
-
puts "quick check feb/29 dates"
|
|
233
|
-
pp [d,m,y]
|
|
234
|
-
pp start
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
if y.nil? ## try to calculate year
|
|
238
|
-
y = if m > start.month ||
|
|
239
|
-
(m == start.month && d >= start.day)
|
|
240
|
-
# assume same year as start_at event (e.g. 2013 for 2013/14 season)
|
|
241
|
-
start.year
|
|
242
|
-
else
|
|
243
|
-
# assume year+1 as start_at event (e.g. 2014 for 2013/14 season)
|
|
244
|
-
start.year+1
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
Date.new( y,m,d ) ## y,m,d
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def on_round_def( node )
|
|
255
|
-
logger.debug "on round def: >#{node}<"
|
|
256
|
-
|
|
257
|
-
## e.g. [[:round_def, "Matchday 1"], [:duration, "Fri Jun/14 - Tue Jun/18"]]
|
|
258
|
-
## [[:round_def, "Matchday 2"], [:duration, "Wed Jun/19 - Sat Jun/22"]]
|
|
259
|
-
## [[:round_def, "Matchday 3"], [:duration, "Sun Jun/23 - Wed Jun/26"]]
|
|
260
|
-
|
|
261
|
-
name = node.name
|
|
262
|
-
# NB: use extracted round name for knockout check
|
|
263
|
-
# knockout_flag = is_knockout_round?( name )
|
|
264
|
-
|
|
265
|
-
if node.date
|
|
266
|
-
start_date = end_date = _build_date( m: node.date[:m],
|
|
267
|
-
d: node.date[:d],
|
|
268
|
-
y: node.date[:y],
|
|
269
|
-
start: @start)
|
|
270
|
-
elsif node.duration
|
|
271
|
-
start_date = _build_date( m: node.duration[:start][:m],
|
|
272
|
-
d: node.duration[:start][:d],
|
|
273
|
-
y: node.duration[:start][:y],
|
|
274
|
-
start: @start)
|
|
275
|
-
end_date = _build_date( m: node.duration[:end][:m],
|
|
276
|
-
d: node.duration[:end][:d],
|
|
277
|
-
y: node.duration[:end][:y],
|
|
278
|
-
start: @start)
|
|
279
|
-
else
|
|
280
|
-
puts "!! PARSE ERROR - expected date or duration for round def; got:"
|
|
281
|
-
pp node
|
|
282
|
-
exit 1
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
# note: - NOT needed; start_at and end_at are saved as date only (NOT datetime)
|
|
286
|
-
# set hours,minutes,secs to beginning and end of day (do NOT use default 12.00)
|
|
287
|
-
# e.g. use 00.00 and 23.59
|
|
288
|
-
# start_at = start_at.beginning_of_day
|
|
289
|
-
# end_at = end_at.end_of_day
|
|
290
|
-
|
|
291
|
-
# note: make sure start_at/end_at is date only (e.g. use start_at.to_date)
|
|
292
|
-
# sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)
|
|
293
|
-
|
|
294
|
-
# note - _build_date always returns Date for now - no longer needed!!
|
|
295
|
-
# start_date = start_date.to_date
|
|
296
|
-
# end_date = end_date.to_date
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
## fix:
|
|
300
|
-
## remove knockout_flag - why? why not?
|
|
301
|
-
knockout_flag = false
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
logger.debug " start_date: #{start_date}"
|
|
305
|
-
logger.debug " end_date: #{end_date}"
|
|
306
|
-
logger.debug " name: >#{name}<"
|
|
307
|
-
logger.debug " knockout_flag: #{knockout_flag}"
|
|
308
|
-
|
|
309
|
-
round = Import::Round.new( name: name,
|
|
310
|
-
start_date: start_date,
|
|
311
|
-
end_date: end_date,
|
|
312
|
-
knockout: knockout_flag,
|
|
313
|
-
auto: false )
|
|
314
|
-
|
|
315
|
-
@rounds[ name ] = round
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
def on_round_header( node )
|
|
320
|
-
logger.debug "on round header: >#{node}<"
|
|
321
|
-
|
|
322
|
-
name = node.names[0] ## ignore more names for now
|
|
323
|
-
## fix later - fix more names!!!
|
|
324
|
-
|
|
325
|
-
# name = name.sub( ROUND_EXTRA_WORDS_RE, '' )
|
|
326
|
-
# name = name.strip
|
|
327
|
-
|
|
328
|
-
round = @rounds[ name ]
|
|
329
|
-
if round.nil? ## auto-add / create if missing
|
|
330
|
-
## todo/check: add num (was pos) if present - why? why not?
|
|
331
|
-
round = Import::Round.new( name: name )
|
|
332
|
-
@rounds[ name ] = round
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
## todo/check: if pos match (MUST always match for now)
|
|
336
|
-
@last_round = round
|
|
337
|
-
@last_group = nil # note: reset group to no group - why? why not?
|
|
338
|
-
|
|
339
|
-
## todo/fix/check
|
|
340
|
-
## make round a scope for date(time) - why? why not?
|
|
341
|
-
## reset date/time e.g. @last_date = nil !!!!
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def on_date_header( node )
|
|
345
|
-
logger.debug( "date header: >#{node}<")
|
|
346
|
-
|
|
347
|
-
date = _build_date( m: node.date[:m],
|
|
348
|
-
d: node.date[:d],
|
|
349
|
-
y: node.date[:y],
|
|
350
|
-
start: @start )
|
|
351
|
-
|
|
352
|
-
logger.debug( " date: #{date} with start: #{@start}")
|
|
353
|
-
|
|
354
|
-
@last_date = date # keep a reference for later use
|
|
355
|
-
@last_time = nil
|
|
356
|
-
|
|
357
|
-
### quick "corona" hack - support seasons going beyond 12 month (see swiss league 2019/20 and others!!)
|
|
358
|
-
## find a better way??
|
|
359
|
-
## set @start date to full year (e.g. 1.1.) if date.year is @start.year+1
|
|
360
|
-
## todo/fix: add to linter to check for chronological dates!! - warn if NOT chronological
|
|
361
|
-
### todo/check: just turn on for 2019/20 season or always? why? why not?
|
|
362
|
-
|
|
363
|
-
## todo/fix: add switch back to old @start_org
|
|
364
|
-
## if year is date.year == @start.year-1 -- possible when full date with year set!!!
|
|
365
|
-
=begin
|
|
366
|
-
if @start.month != 1
|
|
367
|
-
if date.year == @start.year+1
|
|
368
|
-
logger.debug( "!! hack - extending start date to full (next/end) year; assumes all dates are chronologigal - always moving forward" )
|
|
369
|
-
@start_org = @start ## keep a copy of the original (old) start date - why? why not? - not used for now
|
|
370
|
-
@start = Date.new( @start.year+1, 1, 1 )
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
=end
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
def on_goal_line( node )
|
|
378
|
-
logger.debug "on goal line: >#{node}<"
|
|
379
|
-
|
|
380
|
-
goals1 = node.goals1
|
|
381
|
-
goals2 = node.goals2
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
pp [goals1,goals2] if debug?
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
## wrap in struct andd add/append to match
|
|
388
|
-
=begin
|
|
389
|
-
class GoalStruct
|
|
390
|
-
######
|
|
391
|
-
# flat struct for goals - one entry per goals
|
|
392
|
-
attr_accessor :name
|
|
393
|
-
attr_accessor :team # 1 or 2 ? check/todo: add team1 or team2 flag?
|
|
394
|
-
attr_accessor :minute, :offset
|
|
395
|
-
attr_accessor :penalty, :owngoal
|
|
396
|
-
attr_accessor :score1, :score2 # gets calculated
|
|
397
|
-
=end
|
|
398
|
-
|
|
399
|
-
goals = []
|
|
400
|
-
goals1.each do |rec|
|
|
401
|
-
rec.minutes.each do |minute|
|
|
402
|
-
goal = Import::Goal.new(
|
|
403
|
-
player: rec.player,
|
|
404
|
-
team: 1,
|
|
405
|
-
minute: minute.m,
|
|
406
|
-
offset: minute.offset,
|
|
407
|
-
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
|
408
|
-
owngoal: minute.og || false
|
|
409
|
-
)
|
|
410
|
-
goals << goal
|
|
411
|
-
end
|
|
412
|
-
end
|
|
413
|
-
goals2.each do |rec|
|
|
414
|
-
rec.minutes.each do |minute|
|
|
415
|
-
goal = Import::Goal.new(
|
|
416
|
-
player: rec.player,
|
|
417
|
-
team: 2,
|
|
418
|
-
minute: minute.m,
|
|
419
|
-
offset: minute.offset,
|
|
420
|
-
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
|
421
|
-
owngoal: minute.og || false
|
|
422
|
-
)
|
|
423
|
-
goals << goal
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
pp goals if debug?
|
|
428
|
-
|
|
429
|
-
## quick & dirty - auto add goals to last match
|
|
430
|
-
## note - for hacky (quick& dirty) multi-line support
|
|
431
|
-
## always append for now
|
|
432
|
-
match = @matches[-1]
|
|
433
|
-
match.goals ||= []
|
|
434
|
-
match.goals += goals
|
|
435
|
-
|
|
436
|
-
## todo/fix
|
|
437
|
-
## sort by minute
|
|
438
|
-
## PLUS auto-fill score1,score2 - why? why not?
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
def on_match_line( node )
|
|
443
|
-
logger.debug( "on match: >#{node}<" )
|
|
444
|
-
|
|
445
|
-
## collect (possible) nodes by type
|
|
446
|
-
num = nil
|
|
447
|
-
num = node.ord if node.ord
|
|
448
|
-
|
|
449
|
-
date = nil
|
|
450
|
-
date = _build_date( m: node.date[:m],
|
|
451
|
-
d: node.date[:d],
|
|
452
|
-
y: node.date[:y],
|
|
453
|
-
start: @start ) if node.date
|
|
454
|
-
|
|
455
|
-
## note - there's no time (-only) type in ruby
|
|
456
|
-
## use string (e.g. '14:56', '1:44')
|
|
457
|
-
## use 01:44 or 1:44 ?
|
|
458
|
-
## check for 0:00 or 24:00 possible?
|
|
459
|
-
time = nil
|
|
460
|
-
time = ('%d:%02d' % [node.time[:h], node.time[:m]]) if node.time
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
### todo/fix
|
|
464
|
-
## add keywords (e.g. ht, ft or such) to Score.new - why? why not?
|
|
465
|
-
## or use new Score.build( ht:, ft:, ) or such - why? why not?
|
|
466
|
-
## pp score
|
|
467
|
-
score = nil
|
|
468
|
-
if node.score
|
|
469
|
-
ht = node.score[:ht] || [nil,nil]
|
|
470
|
-
ft = node.score[:ft] || [nil,nil]
|
|
471
|
-
et = node.score[:et] || [nil,nil]
|
|
472
|
-
p = node.score[:p] || [nil,nil]
|
|
473
|
-
values = [*ht, *ft, *et, *p]
|
|
474
|
-
## pp values
|
|
475
|
-
score = Score.new( *values )
|
|
476
|
-
end
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
status = nil
|
|
480
|
-
status = node.status if node.status ### assume text for now
|
|
481
|
-
## if node_type == :status # e.g. awarded, canceled, postponed, etc.
|
|
482
|
-
## status = node[1]
|
|
483
|
-
#
|
|
484
|
-
## todo - add ## find (optional) match status e.g. [abandoned] or [replay] or [awarded]
|
|
485
|
-
## or [cancelled] or [postponed] etc.
|
|
486
|
-
## status = find_status!( line ) ## todo/check: allow match status also in geo part (e.g. after @) - why? why not?
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
###############
|
|
490
|
-
# add more for ground (and timezone!!!)
|
|
491
|
-
more = []
|
|
492
|
-
#
|
|
493
|
-
# elsif node_type == :'@' ||
|
|
494
|
-
# node_type == :',' ||
|
|
495
|
-
# node_type == :geo ||
|
|
496
|
-
# node_type == :timezone
|
|
497
|
-
## e.g.
|
|
498
|
-
## [:"@"], [:geo, "Stade de France"], [:","], [:geo, "Saint-Denis"]]
|
|
499
|
-
## [:"@"], [:geo, "Arena de São Paulo"], [:","], [:geo, "São Paulo"], [:timezone, "(UTC-3)"]
|
|
500
|
-
# more << node[1] if node_type == :geo
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
team1 = node.team1
|
|
504
|
-
team2 = node.team2
|
|
505
|
-
|
|
506
|
-
@teams[ team1 ] += 1
|
|
507
|
-
@teams[ team2 ] += 1
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
###
|
|
511
|
-
# check if date found?
|
|
512
|
-
# note: ruby falsey is nil & false only (not 0 or empty array etc.)
|
|
513
|
-
if date
|
|
514
|
-
### check: use date_v2 if present? why? why not?
|
|
515
|
-
@last_date = date # keep a reference for later use
|
|
516
|
-
@last_time = nil
|
|
517
|
-
# @last_time = nil
|
|
518
|
-
else
|
|
519
|
-
date = @last_date # no date found; (re)use last seen date
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
if time
|
|
523
|
-
@last_time = time
|
|
524
|
-
else
|
|
525
|
-
time = @last_time
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
round = nil
|
|
530
|
-
if @last_round
|
|
531
|
-
round = @last_round
|
|
532
|
-
else
|
|
533
|
-
## find (first) matching round by date if rounds / matchdays defined
|
|
534
|
-
## if not rounds / matchdays defined - YES, allow matches WITHOUT rounds!!!
|
|
535
|
-
if @rounds.size > 0
|
|
536
|
-
@rounds.values.each do |round_rec|
|
|
537
|
-
## note: convert date to date only (no time) with to_date!!!
|
|
538
|
-
if (round_rec.start_date && round_rec.end_date) &&
|
|
539
|
-
(date.to_date >= round_rec.start_date &&
|
|
540
|
-
date.to_date <= round_rec.end_date)
|
|
541
|
-
round = round_rec
|
|
542
|
-
break
|
|
543
|
-
end
|
|
544
|
-
end
|
|
545
|
-
if round.nil?
|
|
546
|
-
puts "!! PARSE ERROR - no matching round found for match date:"
|
|
547
|
-
pp date
|
|
548
|
-
exit 1
|
|
549
|
-
end
|
|
550
|
-
end
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
## todo/check: scores are integers or strings?
|
|
554
|
-
|
|
555
|
-
## todo/check: pass along round and group refs or just string (canonical names) - why? why not?
|
|
556
|
-
|
|
557
|
-
## split date in date & time if DateTime
|
|
558
|
-
=begin
|
|
559
|
-
time_str = nil
|
|
560
|
-
date_str = nil
|
|
561
|
-
if date.is_a?( DateTime )
|
|
562
|
-
date_str = date.strftime('%Y-%m-%d')
|
|
563
|
-
time_str = date.strftime('%H:%M')
|
|
564
|
-
elsif date.is_a?( Date )
|
|
565
|
-
date_str = date.strftime('%Y-%m-%d')
|
|
566
|
-
else # assume date is nil
|
|
567
|
-
end
|
|
568
|
-
=end
|
|
569
|
-
|
|
570
|
-
time_str = nil
|
|
571
|
-
date_str = nil
|
|
572
|
-
|
|
573
|
-
date_str = date.strftime('%Y-%m-%d') if date
|
|
574
|
-
time_str = time if date && time
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
ground = nil
|
|
578
|
-
timezone = nil
|
|
579
|
-
if node.geo
|
|
580
|
-
ground = node.geo
|
|
581
|
-
## note: only add/check for timezone if geo (aka ground) is present - why? why not?
|
|
582
|
-
timezone = node.timezone if node.timezone
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
@matches << Import::Match.new( num: num,
|
|
587
|
-
date: date_str,
|
|
588
|
-
time: time_str,
|
|
589
|
-
team1: team1, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.name)
|
|
590
|
-
team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.name)
|
|
591
|
-
score: score,
|
|
592
|
-
round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event)
|
|
593
|
-
group: @last_group ? @last_group.name : nil, ## note: for now always use string (assume unique canonical name for event)
|
|
594
|
-
status: status,
|
|
595
|
-
ground: ground,
|
|
596
|
-
timezone: timezone )
|
|
597
|
-
### todo: cache team lookups in hash?
|
|
598
|
-
end
|
|
599
74
|
end # class MatchParser
|
|
600
75
|
end # module SportDb
|
|
601
76
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module SportDb
|
|
2
|
+
class MatchTree
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Goal ### nested (non-freestanding) inside match (match is parent)
|
|
7
|
+
attr_reader :team, ## note - 1|2 expected
|
|
8
|
+
:player,
|
|
9
|
+
:minute,
|
|
10
|
+
:offset,
|
|
11
|
+
:owngoal, ## true|false
|
|
12
|
+
:penalty ## true|false
|
|
13
|
+
|
|
14
|
+
## add alias for player => name - why? why not?
|
|
15
|
+
alias_method :name, :player
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def owngoal?() @owngoal==true; end
|
|
19
|
+
def penalty?() @penalty==true; end
|
|
20
|
+
def team1?() @team == 1; end
|
|
21
|
+
def team2?() @team == 2; end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## note: make score1,score2 optional for now !!!!
|
|
25
|
+
def initialize( team:,
|
|
26
|
+
player:,
|
|
27
|
+
minute:,
|
|
28
|
+
offset: nil,
|
|
29
|
+
owngoal: false,
|
|
30
|
+
penalty: false
|
|
31
|
+
)
|
|
32
|
+
@team = team # 1|2
|
|
33
|
+
@player = player
|
|
34
|
+
@minute = minute
|
|
35
|
+
@offset = offset
|
|
36
|
+
@owngoal = owngoal
|
|
37
|
+
@penalty = penalty
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def state
|
|
41
|
+
[@team,
|
|
42
|
+
@player, @minute, @offset, @owngoal, @penalty
|
|
43
|
+
]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ==(o)
|
|
47
|
+
o.class == self.class && o.state == state
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pretty_print( printer )
|
|
51
|
+
buf = String.new
|
|
52
|
+
buf << "<Goal"
|
|
53
|
+
buf << " #{@player} #{@minute}"
|
|
54
|
+
buf << "+#{@offset}" if @offset && @offset > 0
|
|
55
|
+
buf << "'"
|
|
56
|
+
buf << " (og)" if @owngoal
|
|
57
|
+
buf << " (p)" if @penalty
|
|
58
|
+
buf << " for #{@team}" ### team 1 or 2 - use home/away
|
|
59
|
+
buf << ">"
|
|
60
|
+
|
|
61
|
+
printer.text( buf )
|
|
62
|
+
end
|
|
63
|
+
end # class Goal
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
end # class MatchTree
|
|
67
|
+
end # module SportDb
|