sportdb-quick 0.3.0 → 0.5.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 +0 -2
- data/Rakefile +1 -1
- data/lib/sportdb/quick/match_parser.rb +135 -271
- data/lib/sportdb/quick/version.rb +1 -1
- data/lib/sportdb/quick.rb +0 -4
- metadata +5 -9
- data/bin/fbt +0 -170
- data/bin/fbx +0 -152
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d352420645cf51612b5f1c1185f955ac0766d6bb26d84cae1a9e2adc16b2bd1e
|
4
|
+
data.tar.gz: fa83e99ca9c5b86ad8f9a101513d537e52338d03e181809579ef06ad03788cff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f962bf9735b43bf53aa733c7543679f00daa08582862b4c4624fdc96556d148a2bcb2ea7a16f7a9637bae908cd71591bbc6f0b2fd7b7a0124d3106712a9094b
|
7
|
+
data.tar.gz: 818778f17464a71e2e8f7997661a00d4fd0844a5ebffdb22eb7800590f55bf7a7ccbd49aefe7fdd2d325eef84cb4669cdfb1287b5f819d5e5d6aae438c7ae759
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -88,8 +88,6 @@ class MatchParser ## simple match parser for team match schedules
|
|
88
88
|
@last_round = nil
|
89
89
|
@last_group = nil
|
90
90
|
|
91
|
-
## last_goals - rename to (longer) @last_team_goals or such - why? why not?
|
92
|
-
@last_goals = 1 ## toggle between 1|2 - hacky (quick & dirty) support for multi-line goals, fix soon!
|
93
91
|
|
94
92
|
@teams = Hash.new(0) ## track counts (only) for now for (interal) team stats - why? why not?
|
95
93
|
@rounds = {}
|
@@ -99,12 +97,12 @@ class MatchParser ## simple match parser for team match schedules
|
|
99
97
|
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
100
98
|
|
101
99
|
|
102
|
-
|
103
|
-
@parser = Parser.new
|
104
100
|
@tree = []
|
105
101
|
|
106
102
|
attrib_found = false
|
107
103
|
|
104
|
+
|
105
|
+
lines = []
|
108
106
|
@lines.each_with_index do |line,i|
|
109
107
|
|
110
108
|
if debug?
|
@@ -134,6 +132,9 @@ class MatchParser ## simple match parser for team match schedules
|
|
134
132
|
next
|
135
133
|
end
|
136
134
|
|
135
|
+
lines << line
|
136
|
+
|
137
|
+
=begin
|
137
138
|
t, error_messages = @parser.parse_with_errors( line )
|
138
139
|
|
139
140
|
|
@@ -151,71 +152,47 @@ class MatchParser ## simple match parser for team match schedules
|
|
151
152
|
pp t if debug?
|
152
153
|
|
153
154
|
@tree << t
|
155
|
+
=end
|
154
156
|
end # each lines
|
155
157
|
|
158
|
+
txt = lines.join( "\n") + "\n"
|
159
|
+
parser = RaccMatchParser.new( txt ) ## use own parser instance (not shared) - why? why not?
|
160
|
+
@tree = parser.parse
|
161
|
+
## pp @tree
|
162
|
+
|
156
163
|
## pp @tree
|
157
164
|
|
158
165
|
## report parse errors here - why? why not?
|
159
166
|
|
160
167
|
|
161
168
|
|
162
|
-
@tree.each do |
|
169
|
+
@tree.each do |node|
|
163
170
|
|
164
|
-
|
165
|
-
|
166
|
-
if node_type == :round_def
|
171
|
+
if node.is_a? RaccMatchParser::RoundDef
|
167
172
|
## todo/fix: add round definition (w begin n end date)
|
168
173
|
## todo: do not patch rounds with definition (already assume begin/end date is good)
|
169
174
|
## -- how to deal with matches that get rescheduled/postponed?
|
170
|
-
|
171
|
-
|
175
|
+
on_round_def( node )
|
176
|
+
elsif node.is_a? RaccMatchParser::GroupDef ## NB: group goes after round (round may contain group marker too)
|
172
177
|
### todo: add pipe (|) marker (required)
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
##
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
if node_type == :round
|
190
|
-
node = nodes.shift ## eat-up
|
191
|
-
parse_round_header( node )
|
192
|
-
elsif node_type == :leg
|
193
|
-
node = nodes.shift ## eat-up
|
194
|
-
## ignore (round) leg for now - add later leg - 1|2|3 etc!!!
|
195
|
-
## needs to get added to db/schema too!!!!
|
196
|
-
## add @last_leg = nil or 1|2|3 etc.
|
197
|
-
elsif node_type == :group
|
198
|
-
## -- lets you set group e.g. Group A etc.
|
199
|
-
node = nodes.shift ## eat-up
|
200
|
-
parse_group_header( node )
|
201
|
-
elsif node_type == :date
|
202
|
-
node = nodes.shift ## eat-up
|
203
|
-
parse_date_header( node )
|
204
|
-
## add time here too - why? why not?
|
205
|
-
## add skip comma separator here too - why? why not?
|
206
|
-
## "slurp-up" in upstream parser?
|
207
|
-
## e.g. round, group or group, round ?
|
208
|
-
else
|
209
|
-
break
|
210
|
-
end
|
211
|
-
end
|
212
|
-
next if nodes.empty?
|
213
|
-
|
214
|
-
## rename to try_parse_match - why? why not?
|
215
|
-
parse_match( nodes )
|
178
|
+
on_group_def( node )
|
179
|
+
elsif node.is_a? RaccMatchParser::GroupHeader
|
180
|
+
on_group_header( node )
|
181
|
+
elsif node.is_a? RaccMatchParser::RoundHeader
|
182
|
+
on_round_header( node )
|
183
|
+
elsif node.is_a? RaccMatchParser::DateHeader
|
184
|
+
on_date_header( node )
|
185
|
+
elsif node.is_a? RaccMatchParser::MatchLine
|
186
|
+
on_match_line( node )
|
187
|
+
elsif node.is_a? RaccMatchParser::GoalLine
|
188
|
+
on_goal_line( node )
|
189
|
+
else
|
190
|
+
## report error
|
191
|
+
puts "!! ERROR - unknown node (parse tree type)"
|
192
|
+
pp node
|
193
|
+
exit 1
|
216
194
|
end
|
217
|
-
|
218
|
-
end # tree.each
|
195
|
+
end # tree.each
|
219
196
|
|
220
197
|
## note - team keys are names and values are "internal" stats!!
|
221
198
|
## and NOT team/club/nat_team structs!!
|
@@ -224,8 +201,8 @@ class MatchParser ## simple match parser for team match schedules
|
|
224
201
|
|
225
202
|
|
226
203
|
|
227
|
-
def
|
228
|
-
logger.debug "
|
204
|
+
def on_group_header( node )
|
205
|
+
logger.debug "on group header: >#{node}<"
|
229
206
|
|
230
207
|
# note: group header resets (last) round (allows, for example):
|
231
208
|
# e.g.
|
@@ -235,7 +212,7 @@ class MatchParser ## simple match parser for team match schedules
|
|
235
212
|
# team1 team2 - match (will get new auto-matchday! not last round)
|
236
213
|
@last_round = nil
|
237
214
|
|
238
|
-
name = node
|
215
|
+
name = node.name
|
239
216
|
|
240
217
|
group = @groups[ name ]
|
241
218
|
if group.nil?
|
@@ -248,8 +225,8 @@ class MatchParser ## simple match parser for team match schedules
|
|
248
225
|
end
|
249
226
|
|
250
227
|
|
251
|
-
def
|
252
|
-
logger.debug "
|
228
|
+
def on_group_def( node )
|
229
|
+
logger.debug "on group def: >#{node}<"
|
253
230
|
|
254
231
|
## e.g
|
255
232
|
## [:group_def, "Group A"],
|
@@ -258,26 +235,15 @@ class MatchParser ## simple match parser for team match schedules
|
|
258
235
|
## [:team, "Hungary"],
|
259
236
|
## [:team, "Switzerland"]
|
260
237
|
|
261
|
-
node
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
if node[0] == :team
|
266
|
-
team = node[1]
|
267
|
-
@teams[ team ] += 1
|
268
|
-
team
|
269
|
-
else
|
270
|
-
puts "!! PARSE ERROR - only teams expected in group def; got:"
|
271
|
-
pp nodes
|
272
|
-
exit 1
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
238
|
+
node.teams.each do |team|
|
239
|
+
@teams[ team ] += 1
|
240
|
+
end
|
241
|
+
|
276
242
|
## todo/check/fix: add back group key - why? why not?
|
277
|
-
group = Import::Group.new( name: name,
|
278
|
-
teams: teams )
|
243
|
+
group = Import::Group.new( name: node.name,
|
244
|
+
teams: node.teams )
|
279
245
|
|
280
|
-
@groups[ name ] = group
|
246
|
+
@groups[ node.name ] = group
|
281
247
|
end
|
282
248
|
|
283
249
|
|
@@ -307,37 +273,35 @@ class MatchParser ## simple match parser for team match schedules
|
|
307
273
|
Date.new( y,m,d ) ## y,m,d
|
308
274
|
end
|
309
275
|
|
310
|
-
|
311
|
-
|
276
|
+
|
277
|
+
def on_round_def( node )
|
278
|
+
logger.debug "on round def: >#{node}<"
|
312
279
|
|
313
280
|
## e.g. [[:round_def, "Matchday 1"], [:duration, "Fri Jun/14 - Tue Jun/18"]]
|
314
281
|
## [[:round_def, "Matchday 2"], [:duration, "Wed Jun/19 - Sat Jun/22"]]
|
315
282
|
## [[:round_def, "Matchday 3"], [:duration, "Sun Jun/23 - Wed Jun/26"]]
|
316
283
|
|
317
|
-
|
318
|
-
name = node[1]
|
284
|
+
name = node.name
|
319
285
|
# NB: use extracted round name for knockout check
|
320
286
|
# knockout_flag = is_knockout_round?( name )
|
321
287
|
|
322
|
-
node
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
d: node[2][:d],
|
327
|
-
y: node[2][:y],
|
288
|
+
if node.date
|
289
|
+
start_date = end_date = _build_date( m: node.date[:m],
|
290
|
+
d: node.date[:d],
|
291
|
+
y: node.date[:y],
|
328
292
|
start: @start)
|
329
|
-
elsif
|
330
|
-
start_date = _build_date( m: node[
|
331
|
-
d: node[
|
332
|
-
y: node[
|
293
|
+
elsif node.duration
|
294
|
+
start_date = _build_date( m: node.duration[:start][:m],
|
295
|
+
d: node.duration[:start][:d],
|
296
|
+
y: node.duration[:start][:y],
|
333
297
|
start: @start)
|
334
|
-
end_date = _build_date( m: node[
|
335
|
-
d: node[
|
336
|
-
y: node[
|
298
|
+
end_date = _build_date( m: node.duration[:end][:m],
|
299
|
+
d: node.duration[:end][:d],
|
300
|
+
y: node.duration[:end][:y],
|
337
301
|
start: @start)
|
338
302
|
else
|
339
303
|
puts "!! PARSE ERROR - expected date or duration for round def; got:"
|
340
|
-
pp
|
304
|
+
pp node
|
341
305
|
exit 1
|
342
306
|
end
|
343
307
|
|
@@ -375,10 +339,11 @@ class MatchParser ## simple match parser for team match schedules
|
|
375
339
|
end
|
376
340
|
|
377
341
|
|
378
|
-
def
|
379
|
-
logger.debug "
|
342
|
+
def on_round_header( node )
|
343
|
+
logger.debug "on round header: >#{node}<"
|
380
344
|
|
381
|
-
name = node[
|
345
|
+
name = node.names[0] ## ignore more names for now
|
346
|
+
## fix later - fix more names!!!
|
382
347
|
|
383
348
|
# name = name.sub( ROUND_EXTRA_WORDS_RE, '' )
|
384
349
|
# name = name.strip
|
@@ -399,12 +364,12 @@ class MatchParser ## simple match parser for team match schedules
|
|
399
364
|
## reset date/time e.g. @last_date = nil !!!!
|
400
365
|
end
|
401
366
|
|
402
|
-
def
|
367
|
+
def on_date_header( node )
|
403
368
|
logger.debug( "date header: >#{node}<")
|
404
369
|
|
405
|
-
date = _build_date( m: node[
|
406
|
-
d: node[
|
407
|
-
y: node[
|
370
|
+
date = _build_date( m: node.date[:m],
|
371
|
+
d: node.date[:d],
|
372
|
+
y: node.date[:y],
|
408
373
|
start: @start )
|
409
374
|
|
410
375
|
logger.debug( " date: #{date} with start: #{@start}")
|
@@ -431,93 +396,17 @@ class MatchParser ## simple match parser for team match schedules
|
|
431
396
|
=end
|
432
397
|
end
|
433
398
|
|
434
|
-
def parse_minutes( nodes )
|
435
|
-
## parse goals by player
|
436
|
-
## may have multiple minutes!!
|
437
|
-
goals = []
|
438
|
-
|
439
|
-
node = nodes.shift ## get player
|
440
|
-
name = node[1]
|
441
|
-
|
442
|
-
loop do
|
443
|
-
goal = {}
|
444
|
-
goal[:name] = name
|
445
|
-
|
446
|
-
node_type = nodes[0][0]
|
447
|
-
if node_type != :minute
|
448
|
-
puts "!! PARSE ERROR - minute expected to follow player (in goal); got #{node_type}:"
|
449
|
-
pp nodes
|
450
|
-
exit 1
|
451
|
-
end
|
452
|
-
|
453
|
-
node = nodes.shift
|
454
|
-
goal[:minute] = node[2][:m]
|
455
|
-
goal[:offset] = node[2][:offset] if node[2][:offset]
|
456
|
-
|
457
|
-
## check for own goal or penalty or such
|
458
|
-
if !nodes.empty?
|
459
|
-
node_type = nodes[0][0]
|
460
|
-
if node_type == :og
|
461
|
-
nodes.shift
|
462
|
-
goal[:og] = true
|
463
|
-
elsif node_type == :pen
|
464
|
-
nodes.shift
|
465
|
-
goal[:pen] = true
|
466
|
-
else
|
467
|
-
# do nothing
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
goals << goal
|
472
|
-
|
473
|
-
## check if another minute ahead; otherwise break
|
474
|
-
break if nodes.empty?
|
475
|
-
|
476
|
-
node_type = nodes[0][0]
|
477
399
|
|
478
|
-
|
479
|
-
|
480
|
-
if node_type == :','
|
481
|
-
nodes.shift
|
482
|
-
node_type = nodes[0][0]
|
483
|
-
end
|
400
|
+
def on_goal_line( node )
|
401
|
+
logger.debug "on goal line: >#{node}<"
|
484
402
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
goals
|
490
|
-
end
|
491
|
-
|
492
|
-
|
493
|
-
def parse_goals( nodes )
|
494
|
-
logger.debug "parse goals: >#{nodes}<"
|
495
|
-
|
496
|
-
goals1 = []
|
497
|
-
goals2 = []
|
498
|
-
|
499
|
-
while !nodes.empty?
|
500
|
-
node_type = nodes[0][0]
|
501
|
-
if node_type == :player
|
502
|
-
more_goals = parse_minutes( nodes )
|
503
|
-
## hacky multi-line support for goals
|
504
|
-
## using last_goal (1|2)
|
505
|
-
@last_goals == 2 ? goals2 += more_goals :
|
506
|
-
goals1 += more_goals
|
507
|
-
elsif node_type == :';' ## team separator
|
508
|
-
nodes.shift # eat-up
|
509
|
-
@last_goals = 2
|
510
|
-
elsif node_type == :none
|
511
|
-
nodes.shift # eat-up
|
512
|
-
else
|
513
|
-
puts "!! PARSE ERROR - unexpected node type in goals;; got #{node_type}:"
|
514
|
-
pp nodes
|
515
|
-
exit 1
|
516
|
-
end
|
517
|
-
end
|
403
|
+
goals1 = node.goals1
|
404
|
+
goals2 = node.goals2
|
518
405
|
|
406
|
+
|
519
407
|
pp [goals1,goals2] if debug?
|
520
408
|
|
409
|
+
|
521
410
|
## wrap in struct andd add/append to match
|
522
411
|
=begin
|
523
412
|
class GoalStruct
|
@@ -532,26 +421,30 @@ class GoalStruct
|
|
532
421
|
|
533
422
|
goals = []
|
534
423
|
goals1.each do |rec|
|
535
|
-
|
536
|
-
|
424
|
+
rec.minutes.each do |minute|
|
425
|
+
goal = Import::Goal.new(
|
426
|
+
player: rec.player,
|
537
427
|
team: 1,
|
538
|
-
minute:
|
539
|
-
offset:
|
540
|
-
penalty:
|
541
|
-
owngoal:
|
428
|
+
minute: minute.m,
|
429
|
+
offset: minute.offset,
|
430
|
+
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
431
|
+
owngoal: minute.og || false
|
542
432
|
)
|
543
|
-
|
433
|
+
goals << goal
|
434
|
+
end
|
544
435
|
end
|
545
436
|
goals2.each do |rec|
|
546
|
-
|
547
|
-
|
437
|
+
rec.minutes.each do |minute|
|
438
|
+
goal = Import::Goal.new(
|
439
|
+
player: rec.player,
|
548
440
|
team: 2,
|
549
|
-
minute:
|
550
|
-
offset:
|
551
|
-
penalty:
|
552
|
-
owngoal:
|
441
|
+
minute: minute.m,
|
442
|
+
offset: minute.offset,
|
443
|
+
penalty: minute.pen || false, # note: pass along/use false NOT nil
|
444
|
+
owngoal: minute.og || false
|
553
445
|
)
|
554
446
|
goals << goal
|
447
|
+
end
|
555
448
|
end
|
556
449
|
|
557
450
|
pp goals if debug?
|
@@ -569,90 +462,65 @@ class GoalStruct
|
|
569
462
|
end
|
570
463
|
|
571
464
|
|
572
|
-
def
|
573
|
-
logger.debug( "
|
465
|
+
def on_match_line( node )
|
466
|
+
logger.debug( "on match: >#{node}<" )
|
574
467
|
|
575
468
|
## collect (possible) nodes by type
|
576
469
|
num = nil
|
470
|
+
num = node.ord if node.ord
|
471
|
+
|
577
472
|
date = nil
|
578
|
-
|
579
|
-
|
473
|
+
date = _build_date( m: node.date[:m],
|
474
|
+
d: node.date[:d],
|
475
|
+
y: node.date[:y],
|
476
|
+
start: @start ) if node.date
|
477
|
+
|
478
|
+
## note - there's no time (-only) type in ruby
|
479
|
+
## use string (e.g. '14:56', '1:44')
|
480
|
+
## use 01:44 or 1:44 ?
|
481
|
+
## check for 0:00 or 24:00 possible?
|
482
|
+
time = ('%d:%02d' % [node.time[:h], node.time[:m]]) if node.time
|
483
|
+
|
484
|
+
|
485
|
+
### todo/fix
|
486
|
+
## add keywords (e.g. ht, ft or such) to Score.new - why? why not?
|
487
|
+
## or use new Score.build( ht:, ft:, ) or such - why? why not?
|
488
|
+
## pp score
|
580
489
|
score = nil
|
490
|
+
if node.score
|
491
|
+
ht = node.score[:ht] || [nil,nil]
|
492
|
+
ft = node.score[:ft] || [nil,nil]
|
493
|
+
et = node.score[:et] || [nil,nil]
|
494
|
+
p = node.score[:p] || [nil,nil]
|
495
|
+
values = [*ht, *ft, *et, *p]
|
496
|
+
## pp values
|
497
|
+
score = Score.new( *values )
|
498
|
+
end
|
499
|
+
|
581
500
|
more = []
|
501
|
+
|
582
502
|
status = nil
|
503
|
+
## if node_type == :status # e.g. awarded, canceled, postponed, etc.
|
504
|
+
## status = node[1]
|
505
|
+
|
583
506
|
|
584
|
-
|
585
|
-
node = nodes.shift
|
586
|
-
node_type = node[0]
|
587
|
-
|
588
|
-
if node_type == :num
|
589
|
-
num = node[1]
|
590
|
-
elsif node_type == :date
|
591
|
-
## note: date wipes out/clear time
|
592
|
-
## time MUST always come after date
|
593
|
-
time = nil
|
594
|
-
date = _build_date( m: node[2][:m],
|
595
|
-
d: node[2][:d],
|
596
|
-
y: node[2][:y],
|
597
|
-
start: @start )
|
598
|
-
elsif node_type == :time
|
599
|
-
## note - there's no time (-only) type in ruby
|
600
|
-
## use string (e.g. '14:56', '1:44')
|
601
|
-
## use 01:44 or 1:44 ?
|
602
|
-
## check for 0:00 or 24:00 possible?
|
603
|
-
time = '%d:%02d' % [node[2][:h], node[2][:m]]
|
604
|
-
elsif node_type == :team
|
605
|
-
teams << node[1]
|
606
|
-
elsif node_type == :score
|
607
|
-
### todo/fix
|
608
|
-
## add keywords (e.g. ht, ft or such) to Score.new - why? why not?
|
609
|
-
## or use new Score.build( ht:, ft:, ) or such - why? why not?
|
610
|
-
ht = node[2][:ht] || [nil,nil]
|
611
|
-
ft = node[2][:ft] || [nil,nil]
|
612
|
-
et = node[2][:et] || [nil,nil]
|
613
|
-
p = node[2][:p] || [nil,nil]
|
614
|
-
values = [*ht, *ft, *et, *p]
|
615
|
-
## pp values
|
616
|
-
|
617
|
-
score = Score.new( *values )
|
618
|
-
## pp score
|
619
|
-
elsif node_type == :status # e.g. awarded, canceled, postponed, etc.
|
620
|
-
status = node[1]
|
621
|
-
elsif node_type == :vs
|
622
|
-
## skip; do nothing
|
623
|
-
##
|
507
|
+
#
|
624
508
|
## todo - add ## find (optional) match status e.g. [abandoned] or [replay] or [awarded]
|
625
509
|
## or [cancelled] or [postponed] etc.
|
626
510
|
## status = find_status!( line ) ## todo/check: allow match status also in geo part (e.g. after @) - why? why not?
|
627
|
-
|
628
|
-
elsif node_type == :'@' ||
|
629
|
-
node_type == :',' ||
|
630
|
-
node_type == :geo ||
|
631
|
-
node_type == :timezone
|
511
|
+
#
|
512
|
+
# elsif node_type == :'@' ||
|
513
|
+
# node_type == :',' ||
|
514
|
+
# node_type == :geo ||
|
515
|
+
# node_type == :timezone
|
632
516
|
## e.g.
|
633
517
|
## [:"@"], [:geo, "Stade de France"], [:","], [:geo, "Saint-Denis"]]
|
634
518
|
## [:"@"], [:geo, "Arena de São Paulo"], [:","], [:geo, "São Paulo"], [:timezone, "(UTC-3)"]
|
635
|
-
more << node[1] if node_type == :geo
|
636
|
-
|
637
|
-
puts "!! PARSE ERROR - unexpected node type #{node_type} in match line; got:"
|
638
|
-
pp node
|
639
|
-
## exit 1
|
640
|
-
@errors << ["PARSE ERROR - unexpected node type #{node_type} in match line; got: #{node.inspect}"]
|
641
|
-
return
|
642
|
-
end
|
643
|
-
end
|
644
|
-
|
519
|
+
# more << node[1] if node_type == :geo
|
520
|
+
|
645
521
|
|
646
|
-
|
647
|
-
|
648
|
-
pp teams
|
649
|
-
## exit 1
|
650
|
-
@errors << ["PARSE ERROR - expected two teams; got #{teams.size}: #{teams.inspect}"]
|
651
|
-
return
|
652
|
-
end
|
653
|
-
|
654
|
-
team1 = teams[0]
|
655
|
-
team2 = teams[1]
|
522
|
+
team1 = node.team1
|
523
|
+
team2 = node.team2
|
656
524
|
|
657
525
|
@teams[ team1 ] += 1
|
658
526
|
@teams[ team2 ] += 1
|
@@ -738,10 +606,6 @@ class GoalStruct
|
|
738
606
|
status: status,
|
739
607
|
ground: ground )
|
740
608
|
### todo: cache team lookups in hash?
|
741
|
-
|
742
|
-
## hacky goals support
|
743
|
-
### reset/toggle 1/2
|
744
|
-
@last_goals = 1
|
745
609
|
end
|
746
610
|
end # class MatchParser
|
747
611
|
end # module SportDb
|
data/lib/sportdb/quick.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sportdb-quick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sportdb-parser
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.5.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.5.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sportdb-structs
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,9 +88,7 @@ dependencies:
|
|
88
88
|
version: '4.2'
|
89
89
|
description: sportdb-quick - football.txt (quick) match readers and more
|
90
90
|
email: gerald.bauer@gmail.com
|
91
|
-
executables:
|
92
|
-
- fbt
|
93
|
-
- fbx
|
91
|
+
executables: []
|
94
92
|
extensions: []
|
95
93
|
extra_rdoc_files:
|
96
94
|
- CHANGELOG.md
|
@@ -101,8 +99,6 @@ files:
|
|
101
99
|
- Manifest.txt
|
102
100
|
- README.md
|
103
101
|
- Rakefile
|
104
|
-
- bin/fbt
|
105
|
-
- bin/fbx
|
106
102
|
- lib/sportdb/quick.rb
|
107
103
|
- lib/sportdb/quick/match_parser.rb
|
108
104
|
- lib/sportdb/quick/quick_league_outline_reader.rb
|
data/bin/fbt
DELETED
@@ -1,170 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
## tip: to test run:
|
4
|
-
## ruby -I ./lib bin/fbt
|
5
|
-
## -or-
|
6
|
-
## ruby -I ../parser/lib -I ./lib bin/fbt
|
7
|
-
## -or-
|
8
|
-
## ruby -I ../parser/lib -I ../sportdb-structs/lib -I ./lib bin/fbt
|
9
|
-
|
10
|
-
|
11
|
-
## our own code
|
12
|
-
require 'sportdb/quick'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
require 'optparse'
|
17
|
-
|
18
|
-
##
|
19
|
-
## read textfile
|
20
|
-
## and dump tokens
|
21
|
-
##
|
22
|
-
## fbt ../openfootball/.../euro.txt
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
args = ARGV
|
28
|
-
opts = { debug: false,
|
29
|
-
file: nil,
|
30
|
-
}
|
31
|
-
|
32
|
-
parser = OptionParser.new do |parser|
|
33
|
-
parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
34
|
-
|
35
|
-
##
|
36
|
-
## check if git has a offline option?? (use same)
|
37
|
-
## check for other tools - why? why not?
|
38
|
-
# parser.on( "-q", "--quiet",
|
39
|
-
# "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
|
40
|
-
# opts[:debug] = false
|
41
|
-
# end
|
42
|
-
parser.on( "--verbose", "--debug",
|
43
|
-
"turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
|
44
|
-
opts[:debug] = true
|
45
|
-
end
|
46
|
-
|
47
|
-
parser.on( "-f FILE", "--file FILE",
|
48
|
-
"read datafiles (pathspecs) via .csv file") do |file|
|
49
|
-
opts[:file] = file
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
parser.parse!( args )
|
55
|
-
|
56
|
-
puts "OPTS:"
|
57
|
-
p opts
|
58
|
-
puts "ARGV:"
|
59
|
-
p args
|
60
|
-
|
61
|
-
|
62
|
-
## todo/check - use packs or projects or such
|
63
|
-
## instead of specs - why? why not?
|
64
|
-
specs = []
|
65
|
-
if opts[:file]
|
66
|
-
recs = read_csv( opts[:file] )
|
67
|
-
pp recs
|
68
|
-
## note - make pathspecs relative to passed in file arg!!!
|
69
|
-
basedir = File.dirname( opts[:file] )
|
70
|
-
recs.each do |rec|
|
71
|
-
paths = SportDb::Parser::Opts.find( rec['path'], dir: basedir )
|
72
|
-
specs << [paths, rec]
|
73
|
-
end
|
74
|
-
else
|
75
|
-
paths = if args.empty?
|
76
|
-
[
|
77
|
-
'../../../openfootball/euro/2021--europe/euro.txt',
|
78
|
-
'../../../openfootball/euro/2024--germany/euro.txt',
|
79
|
-
]
|
80
|
-
else
|
81
|
-
## check for directories
|
82
|
-
## and auto-expand
|
83
|
-
SportDb::Parser::Opts.expand_args( args )
|
84
|
-
end
|
85
|
-
specs << [paths, {}]
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
if opts[:debug]
|
90
|
-
SportDb::QuickMatchReader.debug = true
|
91
|
-
SportDb::MatchParser.debug = true
|
92
|
-
else
|
93
|
-
SportDb::QuickMatchReader.debug = false
|
94
|
-
SportDb::MatchParser.debug = false
|
95
|
-
LogUtils::Logger.root.level = :info
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
specs.each_with_index do |(paths, rec),i|
|
100
|
-
errors = []
|
101
|
-
paths.each_with_index do |path,j|
|
102
|
-
puts "==> [#{j+1}/#{paths.size}] reading >#{path}<..."
|
103
|
-
quick = SportDb::QuickMatchReader.new( read_text( path ) )
|
104
|
-
matches = quick.parse
|
105
|
-
|
106
|
-
if quick.errors?
|
107
|
-
puts "!! #{quick.errors.size} error(s):"
|
108
|
-
pp quick.errors
|
109
|
-
|
110
|
-
quick.errors.each do |err|
|
111
|
-
errors << [ path, *err ] # note: use splat (*) to add extra values (starting with msg)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
puts " #{matches.size} match(es)"
|
115
|
-
end
|
116
|
-
|
117
|
-
if errors.size > 0
|
118
|
-
puts
|
119
|
-
puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
|
120
|
-
pp errors
|
121
|
-
else
|
122
|
-
puts
|
123
|
-
puts " OK - no parse errors in #{paths.size} datafile(s)"
|
124
|
-
end
|
125
|
-
|
126
|
-
## add errors to rec via rec['errors'] to allow
|
127
|
-
## for further processing/reporting
|
128
|
-
rec['errors'] = errors
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
###
|
134
|
-
## generate a report if --file option used
|
135
|
-
if opts[:file]
|
136
|
-
|
137
|
-
buf = String.new
|
138
|
-
|
139
|
-
buf << "# fbt summary report - #{specs.size} dataset(s)\n\n"
|
140
|
-
|
141
|
-
specs.each_with_index do |(paths, rec),i|
|
142
|
-
errors = rec['errors']
|
143
|
-
|
144
|
-
if errors.size > 0
|
145
|
-
buf << "!! #{errors.size} ERROR(S) "
|
146
|
-
else
|
147
|
-
buf << " OK "
|
148
|
-
end
|
149
|
-
buf << "%-20s" % rec['path']
|
150
|
-
buf << " - #{paths.size} datafile(s)"
|
151
|
-
buf << "\n"
|
152
|
-
|
153
|
-
if errors.size > 0
|
154
|
-
buf << errors.pretty_inspect
|
155
|
-
buf << "\n"
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
puts
|
160
|
-
puts "SUMMARY:"
|
161
|
-
puts buf
|
162
|
-
|
163
|
-
# maybe write out in the future?
|
164
|
-
# basedir = File.dirname( opts[:file] )
|
165
|
-
# basename = File.basename( opts[:file], File.extname( opts[:file] ))
|
166
|
-
end
|
167
|
-
|
168
|
-
|
169
|
-
puts "bye"
|
170
|
-
|
data/bin/fbx
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
## tip: to test run:
|
4
|
-
## ruby -I ./lib -I ../parser/lib bin/fbx
|
5
|
-
|
6
|
-
##
|
7
|
-
## todo/fix - use for testing
|
8
|
-
# reads textfile and dumps results in json
|
9
|
-
#
|
10
|
-
# check - keep fbx name or find a differnt name - why? why not?
|
11
|
-
|
12
|
-
|
13
|
-
## our own code
|
14
|
-
require 'sportdb/quick'
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
require 'optparse'
|
19
|
-
|
20
|
-
##
|
21
|
-
## read textfile
|
22
|
-
## and dump match parse results
|
23
|
-
##
|
24
|
-
## fbt ../openfootball/.../euro.txt
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
args = ARGV
|
30
|
-
opts = { debug: false,
|
31
|
-
outline: false }
|
32
|
-
|
33
|
-
parser = OptionParser.new do |parser|
|
34
|
-
parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
35
|
-
|
36
|
-
##
|
37
|
-
## check if git has a offline option?? (use same)
|
38
|
-
## check for other tools - why? why not?
|
39
|
-
|
40
|
-
|
41
|
-
parser.on( "--verbose", "--debug",
|
42
|
-
"turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
|
43
|
-
opts[:debug] = debug
|
44
|
-
end
|
45
|
-
|
46
|
-
parser.on( "--outline",
|
47
|
-
"turn on outline (only) output (default: #{opts[:outline]})" ) do |outline|
|
48
|
-
opts[:outline] = outline
|
49
|
-
end
|
50
|
-
end
|
51
|
-
parser.parse!( args )
|
52
|
-
|
53
|
-
puts "OPTS:"
|
54
|
-
p opts
|
55
|
-
puts "ARGV:"
|
56
|
-
p args
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
paths = if args.empty?
|
63
|
-
[
|
64
|
-
'../../../openfootball/euro/2021--europe/euro.txt',
|
65
|
-
'../../../openfootball/euro/2024--germany/euro.txt',
|
66
|
-
]
|
67
|
-
else
|
68
|
-
## check for directories
|
69
|
-
## and auto-expand
|
70
|
-
|
71
|
-
SportDb::Parser::Opts.expand_args( args )
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
SportDb::MatchParser.debug = true if opts[:debug]
|
81
|
-
|
82
|
-
|
83
|
-
## errors = []
|
84
|
-
|
85
|
-
|
86
|
-
paths.each_with_index do |path,i|
|
87
|
-
puts "==> [#{i+1}/#{paths.size}] reading >#{path}<..."
|
88
|
-
|
89
|
-
txt = read_text( path )
|
90
|
-
secs = SportDb::LeagueOutlineReader.parse( txt )
|
91
|
-
## pp secs
|
92
|
-
|
93
|
-
secs.each_with_index do |sec,j| ## sec(tion)s
|
94
|
-
season = sec[:season]
|
95
|
-
league = sec[:league]
|
96
|
-
stage = sec[:stage]
|
97
|
-
lines = sec[:lines]
|
98
|
-
|
99
|
-
puts " section #{j+1}/#{secs.size} - #{league.name} #{season}, #{stage} - #{lines.size} line(s)"
|
100
|
-
|
101
|
-
next if opts[:outline]
|
102
|
-
|
103
|
-
=begin
|
104
|
-
### check if event info availabe - use start_date;
|
105
|
-
## otherwise we have to guess (use a "synthetic" start_date)
|
106
|
-
event_info = catalog.events.find_by( season: season,
|
107
|
-
league: league )
|
108
|
-
|
109
|
-
start = if event_info && event_info.start_date
|
110
|
-
puts "event info found:"
|
111
|
-
puts " using start date from event: "
|
112
|
-
pp event_info
|
113
|
-
pp event_info.start_date
|
114
|
-
event_info.start_date
|
115
|
-
else
|
116
|
-
=end
|
117
|
-
start = if season.year?
|
118
|
-
Date.new( season.start_year, 1, 1 )
|
119
|
-
else
|
120
|
-
Date.new( season.start_year, 7, 1 )
|
121
|
-
end
|
122
|
-
|
123
|
-
parser = SportDb::MatchParser.new( lines,
|
124
|
-
start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
|
125
|
-
|
126
|
-
auto_conf_teams, matches, rounds, groups = parser.parse
|
127
|
-
|
128
|
-
puts ">>> #{auto_conf_teams.size} teams:"
|
129
|
-
pp auto_conf_teams
|
130
|
-
puts ">>> #{matches.size} matches:"
|
131
|
-
## pp matches
|
132
|
-
puts ">>> #{rounds.size} rounds:"
|
133
|
-
pp rounds
|
134
|
-
puts ">>> #{groups.size} groups:"
|
135
|
-
pp groups
|
136
|
-
end # each secs
|
137
|
-
end # each paths
|
138
|
-
|
139
|
-
=begin
|
140
|
-
if errors.size > 0
|
141
|
-
puts
|
142
|
-
pp errors
|
143
|
-
puts
|
144
|
-
puts "!! #{errors.size} parse error(s) in #{paths.size} datafiles(s)"
|
145
|
-
else
|
146
|
-
puts
|
147
|
-
puts "OK no parse errors found in #{paths.size} datafile(s)"
|
148
|
-
end
|
149
|
-
=end
|
150
|
-
|
151
|
-
puts "bye"
|
152
|
-
|