sportdb-quick 0.5.3 → 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 -590
- 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,80 +8,17 @@ 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
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
80
|
-
### todo/fix - FIX/FIX
|
|
81
|
-
## check start year from first date
|
|
82
|
-
## for now (auot-)update - @start with every date that incl. a year!!!
|
|
83
|
-
@start = start
|
|
84
|
-
@last_year = nil
|
|
85
|
-
|
|
19
|
+
def initialize( txt, start: )
|
|
20
|
+
@txt = txt
|
|
21
|
+
@start = start
|
|
86
22
|
|
|
87
23
|
@errors = []
|
|
88
24
|
end
|
|
@@ -94,23 +30,7 @@ class MatchParser ## simple match parser for team match schedules
|
|
|
94
30
|
def parse
|
|
95
31
|
## note: every (new) read call - resets errors list to empty
|
|
96
32
|
@errors = []
|
|
97
|
-
|
|
98
|
-
@last_date = nil
|
|
99
|
-
@last_time = nil
|
|
100
|
-
@last_round = nil
|
|
101
|
-
@last_group = nil
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@teams = Hash.new(0) ## track counts (only) for now for (interal) team stats - why? why not?
|
|
105
|
-
@rounds = {}
|
|
106
|
-
@groups = {}
|
|
107
|
-
@matches = []
|
|
108
|
-
|
|
109
|
-
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
@tree = []
|
|
113
|
-
|
|
33
|
+
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
|
114
34
|
|
|
115
35
|
if debug?
|
|
116
36
|
puts "lines:"
|
|
@@ -138,517 +58,19 @@ class MatchParser ## simple match parser for team match schedules
|
|
|
138
58
|
=end
|
|
139
59
|
|
|
140
60
|
parser = RaccMatchParser.new( @txt ) ## use own parser instance (not shared) - why? why not?
|
|
141
|
-
|
|
61
|
+
tree = parser.parse
|
|
142
62
|
## pp @tree
|
|
143
63
|
|
|
144
64
|
## report parse errors here - why? why not?
|
|
145
65
|
|
|
146
66
|
|
|
147
67
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if node.is_a? RaccMatchParser::RoundDef
|
|
151
|
-
## todo/fix: add round definition (w begin n end date)
|
|
152
|
-
## todo: do not patch rounds with definition (already assume begin/end date is good)
|
|
153
|
-
## -- how to deal with matches that get rescheduled/postponed?
|
|
154
|
-
on_round_def( node )
|
|
155
|
-
elsif node.is_a? RaccMatchParser::GroupDef ## NB: group goes after round (round may contain group marker too)
|
|
156
|
-
### todo: add pipe (|) marker (required)
|
|
157
|
-
on_group_def( node )
|
|
158
|
-
elsif node.is_a? RaccMatchParser::GroupHeader
|
|
159
|
-
on_group_header( node )
|
|
160
|
-
elsif node.is_a? RaccMatchParser::RoundOutline
|
|
161
|
-
on_round_outline( node )
|
|
162
|
-
elsif node.is_a? RaccMatchParser::RoundHeader
|
|
163
|
-
on_round_header( node )
|
|
164
|
-
elsif node.is_a? RaccMatchParser::DateHeader
|
|
165
|
-
on_date_header( node )
|
|
166
|
-
elsif node.is_a? RaccMatchParser::MatchLine
|
|
167
|
-
on_match_line( node )
|
|
168
|
-
elsif node.is_a? RaccMatchParser::GoalLine
|
|
169
|
-
on_goal_line( node )
|
|
170
|
-
elsif node.is_a? RaccMatchParser::LineupLine
|
|
171
|
-
## skip lineup for now
|
|
172
|
-
else
|
|
173
|
-
## report error
|
|
174
|
-
msg = "!! WARN - unknown node (parse tree type) - #{node.class.name}"
|
|
175
|
-
puts msg
|
|
176
|
-
pp node
|
|
177
|
-
|
|
178
|
-
log( msg )
|
|
179
|
-
log( node.pretty_inspect )
|
|
180
|
-
end
|
|
181
|
-
end # tree.each
|
|
182
|
-
|
|
183
|
-
## 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!!
|
|
184
69
|
## and NOT team/club/nat_team structs!!
|
|
185
|
-
[@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
|
|
186
73
|
end # method parse
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def on_group_header( node )
|
|
191
|
-
logger.debug "on group header: >#{node}<"
|
|
192
|
-
|
|
193
|
-
# note: group header resets (last) round (allows, for example):
|
|
194
|
-
# e.g.
|
|
195
|
-
# Group Playoffs/Replays -- round header
|
|
196
|
-
# team1 team2 -- match
|
|
197
|
-
# Group B -- group header
|
|
198
|
-
# team1 team2 - match (will get new auto-matchday! not last round)
|
|
199
|
-
@last_round = nil
|
|
200
|
-
|
|
201
|
-
name = node.name
|
|
202
|
-
|
|
203
|
-
group = @groups[ name ]
|
|
204
|
-
if group.nil?
|
|
205
|
-
puts "!! PARSE ERROR - no group def found for >#{name}<"
|
|
206
|
-
exit 1
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# set group for games
|
|
210
|
-
@last_group = group
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def on_group_def( node )
|
|
215
|
-
logger.debug "on group def: >#{node}<"
|
|
216
|
-
|
|
217
|
-
## e.g
|
|
218
|
-
## [:group_def, "Group A"],
|
|
219
|
-
## [:team, "Germany"],
|
|
220
|
-
## [:team, "Scotland"],
|
|
221
|
-
## [:team, "Hungary"],
|
|
222
|
-
## [:team, "Switzerland"]
|
|
223
|
-
|
|
224
|
-
node.teams.each do |team|
|
|
225
|
-
@teams[ team ] += 1
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
## todo/check/fix: add back group key - why? why not?
|
|
229
|
-
group = Import::Group.new( name: node.name,
|
|
230
|
-
teams: node.teams )
|
|
231
|
-
|
|
232
|
-
@groups[ node.name ] = group
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _build_date( m:, d:, y:, start: )
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
## quick debug hack
|
|
240
|
-
if m == 2 && d == 29
|
|
241
|
-
puts "quick check feb/29 dates"
|
|
242
|
-
pp [d,m,y]
|
|
243
|
-
pp start
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if y.nil? ## try to calculate year
|
|
248
|
-
if @last_year ## use new formula
|
|
249
|
-
y = @last_year
|
|
250
|
-
else ## fallback to "old" formula - FIX/FIX remove later
|
|
251
|
-
puts "[deprecated] WARN - do NOT use old year (date) auto-complete; add year to first date"
|
|
252
|
-
y = if m > start.month ||
|
|
253
|
-
(m == start.month && d >= start.day)
|
|
254
|
-
# assume same year as start_at event (e.g. 2013 for 2013/14 season)
|
|
255
|
-
start.year
|
|
256
|
-
else
|
|
257
|
-
# assume year+1 as start_at event (e.g. 2014 for 2013/14 season)
|
|
258
|
-
start.year+1
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
else
|
|
262
|
-
### note - reset @start to new date
|
|
263
|
-
## use @last_year
|
|
264
|
-
@last_year = y
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Date.new( y,m,d ) ## y,m,d
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def on_round_def( node )
|
|
272
|
-
logger.debug "on round def: >#{node}<"
|
|
273
|
-
|
|
274
|
-
## e.g. [[:round_def, "Matchday 1"], [:duration, "Fri Jun/14 - Tue Jun/18"]]
|
|
275
|
-
## [[:round_def, "Matchday 2"], [:duration, "Wed Jun/19 - Sat Jun/22"]]
|
|
276
|
-
## [[:round_def, "Matchday 3"], [:duration, "Sun Jun/23 - Wed Jun/26"]]
|
|
277
|
-
|
|
278
|
-
name = node.name
|
|
279
|
-
# NB: use extracted round name for knockout check
|
|
280
|
-
# knockout_flag = is_knockout_round?( name )
|
|
281
|
-
|
|
282
|
-
if node.date
|
|
283
|
-
start_date = end_date = _build_date( m: node.date[:m],
|
|
284
|
-
d: node.date[:d],
|
|
285
|
-
y: node.date[:y],
|
|
286
|
-
start: @start)
|
|
287
|
-
elsif node.duration
|
|
288
|
-
start_date = _build_date( m: node.duration[:start][:m],
|
|
289
|
-
d: node.duration[:start][:d],
|
|
290
|
-
y: node.duration[:start][:y],
|
|
291
|
-
start: @start)
|
|
292
|
-
end_date = _build_date( m: node.duration[:end][:m],
|
|
293
|
-
d: node.duration[:end][:d],
|
|
294
|
-
y: node.duration[:end][:y],
|
|
295
|
-
start: @start)
|
|
296
|
-
else
|
|
297
|
-
puts "!! PARSE ERROR - expected date or duration for round def; got:"
|
|
298
|
-
pp node
|
|
299
|
-
exit 1
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
# note: - NOT needed; start_at and end_at are saved as date only (NOT datetime)
|
|
303
|
-
# set hours,minutes,secs to beginning and end of day (do NOT use default 12.00)
|
|
304
|
-
# e.g. use 00.00 and 23.59
|
|
305
|
-
# start_at = start_at.beginning_of_day
|
|
306
|
-
# end_at = end_at.end_of_day
|
|
307
|
-
|
|
308
|
-
# note: make sure start_at/end_at is date only (e.g. use start_at.to_date)
|
|
309
|
-
# sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)
|
|
310
|
-
|
|
311
|
-
# note - _build_date always returns Date for now - no longer needed!!
|
|
312
|
-
# start_date = start_date.to_date
|
|
313
|
-
# end_date = end_date.to_date
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
## fix:
|
|
317
|
-
## remove knockout_flag - why? why not?
|
|
318
|
-
knockout_flag = false
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
logger.debug " start_date: #{start_date}"
|
|
322
|
-
logger.debug " end_date: #{end_date}"
|
|
323
|
-
logger.debug " name: >#{name}<"
|
|
324
|
-
logger.debug " knockout_flag: #{knockout_flag}"
|
|
325
|
-
|
|
326
|
-
round = Import::Round.new( name: name,
|
|
327
|
-
start_date: start_date,
|
|
328
|
-
end_date: end_date,
|
|
329
|
-
knockout: knockout_flag,
|
|
330
|
-
auto: false )
|
|
331
|
-
|
|
332
|
-
@rounds[ name ] = round
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def on_round_outline( node )
|
|
339
|
-
logger.debug "on round outline: >#{node}<"
|
|
340
|
-
|
|
341
|
-
## always reset dates - why? why not?
|
|
342
|
-
## note - needs last_date for year
|
|
343
|
-
## track last_year with extra variable
|
|
344
|
-
|
|
345
|
-
name = node.outline
|
|
346
|
-
|
|
347
|
-
round = @rounds[ name ]
|
|
348
|
-
if round.nil? ## auto-add / create if missing
|
|
349
|
-
## todo/check: add num (was pos) if present - why? why not?
|
|
350
|
-
round = Import::Round.new( name: name )
|
|
351
|
-
@rounds[ name ] = round
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
## todo/check: if pos match (MUST always match for now)
|
|
355
|
-
@last_round = round
|
|
356
|
-
@last_group = nil # note: reset group to no group - why? why not?
|
|
357
|
-
|
|
358
|
-
## todo/fix/check
|
|
359
|
-
## make round a scope for date(time) - why? why not?
|
|
360
|
-
## reset date/time e.g. @last_date = nil !!!!
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def on_round_header( node )
|
|
365
|
-
logger.debug "on round header: >#{node}<"
|
|
366
|
-
|
|
367
|
-
### note - auto-add names with - for now (use comma)
|
|
368
|
-
## why? why not?
|
|
369
|
-
### check - use ' - ' for separator - why? why not?
|
|
370
|
-
name = node.names.join( ', ' )
|
|
371
|
-
=begin
|
|
372
|
-
## note: was node.names[0] ## ignore more names for now
|
|
373
|
-
## fix later - fix more names!!!
|
|
374
|
-
=end
|
|
375
|
-
|
|
376
|
-
# name = name.sub( ROUND_EXTRA_WORDS_RE, '' )
|
|
377
|
-
# name = name.strip
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
round = @rounds[ name ]
|
|
382
|
-
if round.nil? ## auto-add / create if missing
|
|
383
|
-
## todo/check: add num (was pos) if present - why? why not?
|
|
384
|
-
round = Import::Round.new( name: name )
|
|
385
|
-
@rounds[ name ] = round
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
## todo/check: if pos match (MUST always match for now)
|
|
389
|
-
@last_round = round
|
|
390
|
-
@last_group = nil # note: reset group to no group - why? why not?
|
|
391
|
-
|
|
392
|
-
## todo/fix/check
|
|
393
|
-
## make round a scope for date(time) - why? why not?
|
|
394
|
-
## reset date/time e.g. @last_date = nil !!!!
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def on_date_header( node )
|
|
398
|
-
logger.debug( "date header: >#{node}<")
|
|
399
|
-
|
|
400
|
-
date = _build_date( m: node.date[:m],
|
|
401
|
-
d: node.date[:d],
|
|
402
|
-
y: node.date[:y],
|
|
403
|
-
start: @start )
|
|
404
|
-
|
|
405
|
-
logger.debug( " date: #{date} with start: #{@start}")
|
|
406
|
-
|
|
407
|
-
@last_date = date # keep a reference for later use
|
|
408
|
-
@last_time = nil
|
|
409
|
-
|
|
410
|
-
### quick "corona" hack - support seasons going beyond 12 month (see swiss league 2019/20 and others!!)
|
|
411
|
-
## find a better way??
|
|
412
|
-
## set @start date to full year (e.g. 1.1.) if date.year is @start.year+1
|
|
413
|
-
## todo/fix: add to linter to check for chronological dates!! - warn if NOT chronological
|
|
414
|
-
### todo/check: just turn on for 2019/20 season or always? why? why not?
|
|
415
|
-
|
|
416
|
-
## todo/fix: add switch back to old @start_org
|
|
417
|
-
## if year is date.year == @start.year-1 -- possible when full date with year set!!!
|
|
418
|
-
=begin
|
|
419
|
-
if @start.month != 1
|
|
420
|
-
if date.year == @start.year+1
|
|
421
|
-
logger.debug( "!! hack - extending start date to full (next/end) year; assumes all dates are chronologigal - always moving forward" )
|
|
422
|
-
@start_org = @start ## keep a copy of the original (old) start date - why? why not? - not used for now
|
|
423
|
-
@start = Date.new( @start.year+1, 1, 1 )
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
=end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
def on_goal_line( node )
|
|
431
|
-
logger.debug "on goal line: >#{node}<"
|
|
432
|
-
|
|
433
|
-
goals1 = node.goals1
|
|
434
|
-
goals2 = node.goals2
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
pp [goals1,goals2] if debug?
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
## wrap in struct andd add/append to match
|
|
441
|
-
=begin
|
|
442
|
-
class GoalStruct
|
|
443
|
-
######
|
|
444
|
-
# flat struct for goals - one entry per goals
|
|
445
|
-
attr_accessor :name
|
|
446
|
-
attr_accessor :team # 1 or 2 ? check/todo: add team1 or team2 flag?
|
|
447
|
-
attr_accessor :minute, :offset
|
|
448
|
-
attr_accessor :penalty, :owngoal
|
|
449
|
-
attr_accessor :score1, :score2 # gets calculated
|
|
450
|
-
=end
|
|
451
|
-
|
|
452
|
-
goals = []
|
|
453
|
-
goals1.each do |rec|
|
|
454
|
-
rec.minutes.each do |minute|
|
|
455
|
-
goal = Import::Goal.new(
|
|
456
|
-
player: rec.player,
|
|
457
|
-
team: 1,
|
|
458
|
-
minute: minute.m,
|
|
459
|
-
offset: minute.offset,
|
|
460
|
-
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
|
461
|
-
owngoal: minute.og || false
|
|
462
|
-
)
|
|
463
|
-
goals << goal
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
goals2.each do |rec|
|
|
467
|
-
rec.minutes.each do |minute|
|
|
468
|
-
goal = Import::Goal.new(
|
|
469
|
-
player: rec.player,
|
|
470
|
-
team: 2,
|
|
471
|
-
minute: minute.m,
|
|
472
|
-
offset: minute.offset,
|
|
473
|
-
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
|
474
|
-
owngoal: minute.og || false
|
|
475
|
-
)
|
|
476
|
-
goals << goal
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
pp goals if debug?
|
|
481
|
-
|
|
482
|
-
## quick & dirty - auto add goals to last match
|
|
483
|
-
## note - for hacky (quick& dirty) multi-line support
|
|
484
|
-
## always append for now
|
|
485
|
-
match = @matches[-1]
|
|
486
|
-
match.goals ||= []
|
|
487
|
-
match.goals += goals
|
|
488
|
-
|
|
489
|
-
## todo/fix
|
|
490
|
-
## sort by minute
|
|
491
|
-
## PLUS auto-fill score1,score2 - why? why not?
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def on_match_line( node )
|
|
496
|
-
logger.debug( "on match: >#{node}<" )
|
|
497
|
-
|
|
498
|
-
## collect (possible) nodes by type
|
|
499
|
-
num = nil
|
|
500
|
-
num = node.ord if node.ord
|
|
501
|
-
|
|
502
|
-
date = nil
|
|
503
|
-
date = _build_date( m: node.date[:m],
|
|
504
|
-
d: node.date[:d],
|
|
505
|
-
y: node.date[:y],
|
|
506
|
-
start: @start ) if node.date
|
|
507
|
-
|
|
508
|
-
## note - there's no time (-only) type in ruby
|
|
509
|
-
## use string (e.g. '14:56', '1:44')
|
|
510
|
-
## use 01:44 or 1:44 ?
|
|
511
|
-
## check for 0:00 or 24:00 possible?
|
|
512
|
-
time = nil
|
|
513
|
-
time = ('%d:%02d' % [node.time[:h], node.time[:m]]) if node.time
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
### todo/fix
|
|
517
|
-
## add keywords (e.g. ht, ft or such) to Score.new - why? why not?
|
|
518
|
-
## or use new Score.build( ht:, ft:, ) or such - why? why not?
|
|
519
|
-
## pp score
|
|
520
|
-
score = nil
|
|
521
|
-
if node.score
|
|
522
|
-
ht = node.score[:ht] || [nil,nil]
|
|
523
|
-
ft = node.score[:ft] || [nil,nil]
|
|
524
|
-
et = node.score[:et] || [nil,nil]
|
|
525
|
-
p = node.score[:p] || [nil,nil]
|
|
526
|
-
values = [*ht, *ft, *et, *p]
|
|
527
|
-
## pp values
|
|
528
|
-
score = Score.new( *values )
|
|
529
|
-
end
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
status = nil
|
|
533
|
-
status = node.status if node.status ### assume text for now
|
|
534
|
-
## if node_type == :status # e.g. awarded, canceled, postponed, etc.
|
|
535
|
-
## status = node[1]
|
|
536
|
-
#
|
|
537
|
-
## todo - add ## find (optional) match status e.g. [abandoned] or [replay] or [awarded]
|
|
538
|
-
## or [cancelled] or [postponed] etc.
|
|
539
|
-
## status = find_status!( line ) ## todo/check: allow match status also in geo part (e.g. after @) - why? why not?
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
###############
|
|
543
|
-
# add more for ground (and timezone!!!)
|
|
544
|
-
more = []
|
|
545
|
-
#
|
|
546
|
-
# elsif node_type == :'@' ||
|
|
547
|
-
# node_type == :',' ||
|
|
548
|
-
# node_type == :geo ||
|
|
549
|
-
# node_type == :timezone
|
|
550
|
-
## e.g.
|
|
551
|
-
## [:"@"], [:geo, "Stade de France"], [:","], [:geo, "Saint-Denis"]]
|
|
552
|
-
## [:"@"], [:geo, "Arena de São Paulo"], [:","], [:geo, "São Paulo"], [:timezone, "(UTC-3)"]
|
|
553
|
-
# more << node[1] if node_type == :geo
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
team1 = node.team1
|
|
557
|
-
team2 = node.team2
|
|
558
|
-
|
|
559
|
-
@teams[ team1 ] += 1
|
|
560
|
-
@teams[ team2 ] += 1
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
###
|
|
564
|
-
# check if date found?
|
|
565
|
-
# note: ruby falsey is nil & false only (not 0 or empty array etc.)
|
|
566
|
-
if date
|
|
567
|
-
### check: use date_v2 if present? why? why not?
|
|
568
|
-
@last_date = date # keep a reference for later use
|
|
569
|
-
@last_time = nil
|
|
570
|
-
# @last_time = nil
|
|
571
|
-
else
|
|
572
|
-
date = @last_date # no date found; (re)use last seen date
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
if time
|
|
576
|
-
@last_time = time
|
|
577
|
-
else
|
|
578
|
-
time = @last_time
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
round = nil
|
|
583
|
-
if @last_round
|
|
584
|
-
round = @last_round
|
|
585
|
-
else
|
|
586
|
-
## find (first) matching round by date if rounds / matchdays defined
|
|
587
|
-
## if not rounds / matchdays defined - YES, allow matches WITHOUT rounds!!!
|
|
588
|
-
if @rounds.size > 0
|
|
589
|
-
@rounds.values.each do |round_rec|
|
|
590
|
-
## note: convert date to date only (no time) with to_date!!!
|
|
591
|
-
if (round_rec.start_date && round_rec.end_date) &&
|
|
592
|
-
(date.to_date >= round_rec.start_date &&
|
|
593
|
-
date.to_date <= round_rec.end_date)
|
|
594
|
-
round = round_rec
|
|
595
|
-
break
|
|
596
|
-
end
|
|
597
|
-
end
|
|
598
|
-
if round.nil?
|
|
599
|
-
puts "!! PARSE ERROR - no matching round found for match date:"
|
|
600
|
-
pp date
|
|
601
|
-
exit 1
|
|
602
|
-
end
|
|
603
|
-
end
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
## todo/check: scores are integers or strings?
|
|
607
|
-
|
|
608
|
-
## todo/check: pass along round and group refs or just string (canonical names) - why? why not?
|
|
609
|
-
|
|
610
|
-
## split date in date & time if DateTime
|
|
611
|
-
=begin
|
|
612
|
-
time_str = nil
|
|
613
|
-
date_str = nil
|
|
614
|
-
if date.is_a?( DateTime )
|
|
615
|
-
date_str = date.strftime('%Y-%m-%d')
|
|
616
|
-
time_str = date.strftime('%H:%M')
|
|
617
|
-
elsif date.is_a?( Date )
|
|
618
|
-
date_str = date.strftime('%Y-%m-%d')
|
|
619
|
-
else # assume date is nil
|
|
620
|
-
end
|
|
621
|
-
=end
|
|
622
|
-
|
|
623
|
-
time_str = nil
|
|
624
|
-
date_str = nil
|
|
625
|
-
|
|
626
|
-
date_str = date.strftime('%Y-%m-%d') if date
|
|
627
|
-
time_str = time if date && time
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
ground = nil
|
|
631
|
-
timezone = nil
|
|
632
|
-
if node.geo
|
|
633
|
-
ground = node.geo
|
|
634
|
-
## note: only add/check for timezone if geo (aka ground) is present - why? why not?
|
|
635
|
-
timezone = node.timezone if node.timezone
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
@matches << Import::Match.new( num: num,
|
|
640
|
-
date: date_str,
|
|
641
|
-
time: time_str,
|
|
642
|
-
team1: team1, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.name)
|
|
643
|
-
team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.name)
|
|
644
|
-
score: score,
|
|
645
|
-
round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event)
|
|
646
|
-
group: @last_group ? @last_group.name : nil, ## note: for now always use string (assume unique canonical name for event)
|
|
647
|
-
status: status,
|
|
648
|
-
ground: ground,
|
|
649
|
-
timezone: timezone )
|
|
650
|
-
### todo: cache team lookups in hash?
|
|
651
|
-
end
|
|
652
74
|
end # class MatchParser
|
|
653
75
|
end # module SportDb
|
|
654
76
|
|