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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7f373cf63aa06b60bfe3f654df254e3e04d5a47c499a6be1dc4f9d90c061f84
4
- data.tar.gz: ec5b8ea56ef5785b4133cd814254a0a4290873b453e806ad27f974dac4ac8594
3
+ metadata.gz: 7b48a679900d31129e10a922ffa2bdea28bd919d6effe565e958d915cfc69a18
4
+ data.tar.gz: '089b0cf75426611e738012751e446cc8b3e319027a6ed4f6872fe1d773467bac'
5
5
  SHA512:
6
- metadata.gz: 19dd7b607f4c3288f176f941a570c1f316956ee2867f1cfebaebcf180506a3d640a797ea723ee889406f8f6682c326a95ab89a0877250b4709fa8bb5b6eb8aef
7
- data.tar.gz: b6a76be55c06acb1f4487794a9097d2f6e1d5ea4414b2f1fabbf32d3f18df2a90cc8024d1c51b8f42d7be1e610ba7543dc9c6b41e3eea4822e16e8b689ca7687
6
+ metadata.gz: 677234ed2602b3b153713c58d4a398c3e73a72d48c19f83667c4fa0764331c8971db60f1091800380c146a10041e281b48df8299ac1d493604f5f5dfc59aebf9
7
+ data.tar.gz: c66fb17fae6acf92fe4aa1177aa3758d8073b7e88f7e25de73942e275b8bf6652a1239fbcea7ee7221c87e87634b3f5d53c312edf97b637e358da3602b1494a7
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.5.2
1
+ ### 0.7.0
2
2
  ### 0.0.1 / 2024-08-27
3
3
 
4
4
  * Everything is new. First release.
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/outline.rb
8
- lib/sportdb/quick/outline_reader.rb
9
- lib/sportdb/quick/quick_league_outline.rb
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.5.4'],
25
- ['sportdb-structs', '>= 0.5.0'],
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
- def self.parse( lines, start: )
26
- ## todo/fix: add support for txt and lines
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
- @tree = parser.parse
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
- @tree.each do |node|
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