fbtxt-document 0.9.0 → 0.9.1

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.
@@ -0,0 +1,72 @@
1
+
2
+ module Fbtxt
3
+ module Model
4
+
5
+
6
+ ##############
7
+ ## e.g.
8
+ ## 'Y', Player, 44
9
+ ## 'YR', Player, 45+2
10
+
11
+ class EventCard ### use EventBooking - why? why not?
12
+ attr_reader :type
13
+ attr_reader :name
14
+ attr_reader :minute
15
+
16
+ def initialize( type:, name:, minute: nil )
17
+ @type = type
18
+ @name = name
19
+
20
+ ## note - auto-convert to our own Minute format
21
+ @minute = if minute.is_a?( RaccMatchParser::Minute )
22
+ Minute.new( m: minute.m,
23
+ offset: minute.offset,
24
+ secs: minute.secs )
25
+ else
26
+ minute
27
+ end
28
+ end
29
+
30
+
31
+ def as_json(*)
32
+ h = { 'type' => type,
33
+ 'name' => name }
34
+ h['minute'] = minute.to_s if minute
35
+
36
+ h
37
+ end
38
+ end # class EventCard
39
+
40
+
41
+ class EventSub ## use just Sub or ??
42
+
43
+ attr_reader :name_on ## use on only - why? why not?
44
+ attr_reader :name_off ## use off only
45
+ attr_reader :minute
46
+
47
+ def initialize( on:, off:, minute: nil )
48
+ @name_on = on
49
+ @name_off = off
50
+
51
+ ## note - auto-convert to our own Minute format
52
+ @minute = if minute.is_a?( RaccMatchParser::Minute )
53
+ Minute.new( m: minute.m,
54
+ offset: minute.offset,
55
+ secs: minute.secs )
56
+ else
57
+ minute
58
+ end
59
+ end
60
+
61
+ def as_json(*)
62
+ h = { 'on' => name_on,
63
+ 'off' => name_off }
64
+ h['minute'] = minute.to_s if minute
65
+
66
+ h
67
+ end
68
+ end ## EventSub
69
+
70
+
71
+ end # module Model
72
+ end # module Fbtxt
@@ -0,0 +1,94 @@
1
+ module Fbtxt
2
+ module Model
3
+
4
+
5
+ class Goal ### nested (non-freestanding) inside match (match is parent)
6
+
7
+ attr_reader :team, ## note - 1|2 expected
8
+ :name,
9
+ :minute,
10
+ :owngoal, ## true|false
11
+ :penalty ## true|false
12
+
13
+ ## add alias for player => name - why? why not?
14
+ ## alias_method :player, :name
15
+
16
+ def owngoal?() @owngoal==true; end
17
+ def penalty?() @penalty==true; end
18
+ def team1?() @team == 1; end
19
+ def team2?() @team == 2; end
20
+
21
+
22
+ def initialize( team:,
23
+ name:,
24
+ minute: nil,
25
+ owngoal: false,
26
+ penalty: false
27
+ )
28
+ @team = team # 1|2
29
+ @name = name
30
+
31
+ ## note - auto-convert to our own Minute format
32
+ ## is obj/incl. (optional) offset/injury time!!
33
+ @minute = if minute.is_a?( RaccMatchParser::Minute )
34
+ Minute.new( m: minute.m,
35
+ offset: minute.offset,
36
+ secs: minute.secs )
37
+ else
38
+ minute
39
+ end
40
+
41
+ @owngoal = owngoal
42
+ @penalty = penalty
43
+ end
44
+
45
+ def state
46
+ [@team,
47
+ @name, @minute, @owngoal, @penalty
48
+ ]
49
+ end
50
+
51
+ def ==(o)
52
+ o.class == self.class && o.state == state
53
+ end
54
+
55
+ def pretty_print( q )
56
+ q.group( 4, '<Goal ', '>') do ## group( indent, open, close)
57
+ q.text( name )
58
+ q.text( " #{@minute}" ) if @minute
59
+
60
+ q.text( "(og)" ) if @owngoal
61
+ q.text( "(p)" ) if @penalty
62
+ q.text( " for #{@team}" ) ### team 1 or 2 - use home/away
63
+ end
64
+ end
65
+
66
+
67
+ ### note - EXCLUDE team 1|2 from serialize
68
+ ## maybe remove alltogether and split into goals[[],[]] - why? why not?
69
+ def as_json(*)
70
+ h = { 'name' => name }
71
+
72
+ =begin
73
+ ## note - use a string for minutes for now
74
+ ## allows e.g. 45+2 etc. too
75
+ minute_str = "#{goal.minute}"
76
+ minute_str += "+#{goal.offset}" if goal.offset
77
+
78
+ node['minute'] = minute_str
79
+ =end
80
+
81
+ h['minute'] = minute.to_s if minute
82
+
83
+ h['owngoal'] = true if owngoal?
84
+ h['penalty'] = true if penalty?
85
+
86
+ h
87
+ end
88
+
89
+
90
+ end # class Goal
91
+
92
+
93
+ end # module Model
94
+ end # module Fbtxt
@@ -1,5 +1,5 @@
1
1
  module Fbtxt
2
- class MatchTree
2
+ module Model
3
3
 
4
4
  class Group
5
5
  attr_reader :name, :teams
@@ -19,5 +19,5 @@ class Group
19
19
  end # class Group
20
20
 
21
21
 
22
- end # class MatchTree
22
+ end # module Model
23
23
  end # module Fbtxt
@@ -0,0 +1,147 @@
1
+ =begin
2
+
3
+ (double) check bookings
4
+ - is trainer or co-trainer - why NOT included in lineup ???
5
+ ==> reading >austria/2025-26/1-bundesliga-full.txt<...
6
+ !! warn - player >Thomas Turner< NOT found; skipping booking Thomas Turner 90+3
7
+ !! warn - player >Thomas Turner< NOT found; skipping booking Thomas Turner 88
8
+ !! warn - player >Tobias Schützenauer< NOT found; skipping booking Tobias Schützenauer 90+5
9
+ !! warn - player >Jeyland MITCHELL< NOT found; skipping booking Jeyland MITCHELL 77
10
+ !! warn - player >Lukas Gütlbauer< NOT found; skipping booking Lukas Gütlbauer 84
11
+ =end
12
+
13
+
14
+
15
+ module Fbtxt
16
+ module Model
17
+
18
+ =begin
19
+ ## fix-fix-fix
20
+ ## move into (nested inside) Lineup - why? why not?
21
+ class Card
22
+ attr_reader :name, :minute
23
+ def initialize( name:, minute: nil )
24
+ @name = name
25
+ @minute = minute
26
+ end
27
+ end ## class Card
28
+ =end
29
+
30
+
31
+
32
+
33
+ ## note - lineup incl. starter & (optional) bench & sub(stituion)s
34
+ class Lineup
35
+
36
+
37
+
38
+ def self.build( lineup )
39
+ ## add starter & bench (via substitutions)
40
+ starter = _collect_starter( lineup )
41
+ bench = _collect_bench( lineup )
42
+ subs = _collect_subs( lineup )
43
+
44
+ new( starter: starter,
45
+ bench: bench,
46
+ subs: subs )
47
+ end
48
+
49
+
50
+ def self._collect_starter( lineup )
51
+ recs = []
52
+ lineup.each do |item|
53
+ ## puts " lineup.each item:"
54
+ ## pp item
55
+ rec = Player.new( name: item.name,
56
+ captain: item.captain )
57
+
58
+ recs << rec
59
+ end
60
+ recs
61
+ end
62
+
63
+ def self._collect_bench( lineup )
64
+ recs = []
65
+ lineup.each do |item|
66
+ current = item
67
+ while current && current.sub
68
+ rec = Player.new( name: current.sub.sub.name,
69
+ captain: current.sub.sub.captain )
70
+
71
+ recs << rec
72
+ current = current.sub.sub
73
+ end
74
+ end
75
+ recs
76
+ end
77
+
78
+ def self._collect_subs( lineup )
79
+ recs = []
80
+ lineup.each do |item|
81
+ current = item
82
+ while current && current.sub
83
+ ## fix-fix-fix - use player-ref for off/on
84
+ rec = EventSub.new( off: current.name,
85
+ on: current.sub.sub.name,
86
+ minute: current.sub.minute )
87
+ recs << rec
88
+ current = current.sub.sub
89
+ end
90
+ end
91
+
92
+ ## note - (auto-)sort by minute if present
93
+ ### fix-fix-fix move sort to EventSub <=> - why? why not?
94
+ recs = recs.sort do |l,r|
95
+ if l.minute && r.minute
96
+ l.minute <=> r.minute
97
+ else
98
+ 0 ## keep as is (no minutes available)
99
+ end
100
+ end
101
+ recs
102
+ end
103
+
104
+
105
+
106
+
107
+
108
+ def initialize( starter:,
109
+ bench: [],
110
+ subs: [] )
111
+ @starter = {} ## note - indexed by (player) name
112
+ starter.each do |player|
113
+ @starter[player.name] = player
114
+ end
115
+
116
+ @bench = {} ## note - indexed by (player) name
117
+ bench.each do |player|
118
+ @bench[player.name] = player
119
+ end
120
+
121
+ @subs = subs
122
+ end
123
+
124
+ def starter() @starter.values; end
125
+ def bench() @bnech.values; end
126
+ def subs() @subs; end
127
+
128
+
129
+ def find_player( name )
130
+ player = @starter[name] || @bench[name]
131
+ end
132
+
133
+
134
+
135
+ def as_json(*)
136
+ h = { 'starter' => @starter.values.as_json }
137
+
138
+ h['bench'] = @bench.values.as_json if @bench.size > 0
139
+ h['subs'] = @subs.as_json if @subs.size > 0
140
+
141
+ h
142
+ end
143
+ end # class Lineup
144
+
145
+
146
+ end # module Model
147
+ end # module Fbtxt
@@ -0,0 +1,194 @@
1
+ ##
2
+ # move (simpler) struct version inline to MatchTree for now
3
+ #
4
+ module Fbtxt
5
+ module Model
6
+
7
+
8
+
9
+ class Match
10
+
11
+ attr_reader :num,
12
+ :date,
13
+ :time,
14
+ :time_local,
15
+ :team1, :team2, ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
16
+ :score,
17
+ :round, ## todo/fix: use round_num or similar - for compat with db activerecord version? why? why not?
18
+ :group,
19
+ :status, ## e.g. replay, cancelled, awarded, abadoned, postponed, etc.
20
+ :ground, ## (optional) add as text line for now (incl. city, timezone etc.)
21
+ :att ## (optional) attendance as (integer) number
22
+
23
+
24
+ ## e.g. support (flat/undefined) [], or (nested/paired) [[],[]]
25
+ attr_accessor :goals ## todo/fix: make goals like all other attribs!!
26
+
27
+ ###
28
+ # add details match (report) info
29
+ ## lineup,bench,subs,bookings(y/yr/r/etc.)
30
+ attr_accessor :lineup ## incl. starter/bench/subs!!!
31
+ attr_accessor :bookings ## use cards - why? why not?
32
+
33
+ attr_accessor :referees
34
+
35
+ attr_accessor :penalties
36
+
37
+
38
+ def initialize( **kwargs )
39
+ update( **kwargs ) unless kwargs.empty?
40
+ end
41
+
42
+
43
+ def update( **kwargs )
44
+ @num = kwargs[:num] if kwargs.has_key?( :num )
45
+
46
+ ## note: check with has_key? because value might be nil!!!
47
+ @date = kwargs[:date] if kwargs.has_key?( :date )
48
+ @time = kwargs[:time] if kwargs.has_key?( :time )
49
+ @time_local = kwargs[:time_local] if kwargs.has_key?( :time_local )
50
+
51
+ ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
52
+ @team1 = kwargs[:team1] if kwargs.has_key?( :team1 )
53
+ @team2 = kwargs[:team2] if kwargs.has_key?( :team2 )
54
+
55
+ ## note: round is a string!!! e.g. '1', '2' for matchday or 'Final', 'Semi-final', etc.
56
+ ## todo: use to_s - why? why not?
57
+ @round = kwargs[:round] if kwargs.has_key?( :round )
58
+ @group = kwargs[:group] if kwargs.has_key?( :group )
59
+ @status = kwargs[:status] if kwargs.has_key?( :status )
60
+
61
+ @ground = kwargs[:ground] if kwargs.has_key?( :ground )
62
+ @att = kwargs[:att] if kwargs.has_key?( :att )
63
+
64
+ ## check/expects all-in-one score struct for convenience!!!
65
+ @score = kwargs[:score] if kwargs.has_key?( :score )
66
+
67
+ self ## note - MUST return self for chaining
68
+ end
69
+
70
+
71
+
72
+ def as_json
73
+ #####
74
+ ## note - use string keys (NOT symbol for data keys)
75
+ ## for easier json compatibility
76
+ data = {}
77
+
78
+ ## check round
79
+ if @round
80
+ data['round'] = if round.is_a?( Integer )
81
+ "Matchday #{@round}"
82
+ else ## assume string
83
+ @round
84
+ end
85
+ end
86
+
87
+
88
+ data['num'] = @num if @num
89
+
90
+
91
+ if @date
92
+ ## assume 2020-09-19 date format!!
93
+ data['date'] = @date.is_a?( String ) ? @date : @date.strftime('%Y-%m-%d')
94
+
95
+ data['time'] = @time if @time
96
+ data['time_local'] = @time_local if @time_local
97
+ end
98
+
99
+
100
+ data['team1'] = @team1.is_a?( String ) ? @team1 : @team1.name
101
+
102
+ ## note - for match status bye team2 is nil!!!
103
+ ## e.g. Queen's Park bye
104
+ ## Wanderers bye
105
+ ## todo/check - keep bye as a match - why? why not?
106
+ ## has no date/time & venue & score etc.
107
+ if @team2
108
+ data['team2'] = @team2.is_a?( String ) ? @team2 : @team2.name
109
+ end
110
+
111
+ ## note - score might be
112
+ ## 1) array e.g. [0,1] -- e.g. uses reported key internally
113
+ ## 2) hash e.g. { 'ft' [0,1] } etc.
114
+ ## note - w/o (walkout) do NOT add empty score
115
+
116
+ ## note:
117
+ ## use/assume full-time (ft) if reported - why? why not?
118
+ ## in future check for score note or such
119
+ ## to use "plain" array or such - why? why not?
120
+
121
+ data['score'] = @score.as_json if @score
122
+
123
+
124
+
125
+ ### check for goals
126
+ if @goals.is_a?(Array) && @goals.size > 0
127
+ data['goals1'] = []
128
+ data['goals2'] = []
129
+
130
+ @goals.each do |goal|
131
+ if goal.team == 1
132
+ data['goals1'] << goal.as_json
133
+ else ## assume 2 (or better raise except if NOT 2)
134
+ data['goals2'] << goal.as_json
135
+ end
136
+ end # each goal
137
+ end
138
+
139
+ ### check for penalties (shootout)
140
+ if @penalties.is_a?(Array) && @penalties.size > 0
141
+ data['penalties'] = @penalties.as_json
142
+ end
143
+
144
+
145
+
146
+ data['status'] = @status if @status
147
+
148
+ data['group'] = @group if @group
149
+
150
+ if @ground
151
+ ## note: might be array of string e.g. ['Wembley', 'London']
152
+ ##
153
+ ## todo/check - auto-join to string - why? why not?
154
+ ## e.g. ['Wembley', 'London']
155
+ ## to 'Wembley, London'
156
+ ## note - auto-join geo tree for now
157
+ data['ground'] = @ground.join(', ')
158
+ end
159
+
160
+ data['attendance'] = @att if @att
161
+
162
+
163
+ ###
164
+ # check for match (report) details
165
+ # quick & dirty first try/version/dump
166
+ # refine (serialize) format later
167
+ if @lineup.is_a?(Array) && @lineup.size > 0
168
+ data['lineup'] = @lineup.as_json
169
+ end
170
+
171
+
172
+ if @bookings.is_a?(Array) && @bookings.size > 0
173
+ if @bookings[0].empty? && @bookings[1].empty?
174
+ ## skip empty bookings
175
+ ## "bookings": [[], []]
176
+ else
177
+ data['bookings'] = @bookings.as_json
178
+ end
179
+ end
180
+
181
+
182
+ if @referees.is_a?(Array) && @referees.size > 0
183
+ data['referees'] = @referees.as_json
184
+ end
185
+
186
+
187
+ data
188
+ end
189
+ end # class Match
190
+
191
+
192
+
193
+ end # module Model
194
+ end # module Fbtxt
@@ -0,0 +1,57 @@
1
+ module Fbtxt
2
+ module Model
3
+
4
+
5
+ class Minute
6
+
7
+ attr_reader :m, :offset, :secs
8
+
9
+ def initialize( m:, offset: nil, secs: nil )
10
+ @m = m
11
+ @offset = offset
12
+ @secs = secs
13
+ end
14
+
15
+ ## todo/check - rename offset to injury/stoppage (time) or such - why? why not?
16
+ =begin
17
+ Alternative naming conventions
18
+ here are the three best ways to rename it
19
+ - stoppage: Short, clean, and highly readable in a class context
20
+ Minute.new(m: 90, stoppage: 4).
21
+ - stoppage_time: Highly explicit if you want zero ambiguity.
22
+ - added_time: Another officially recognized term that reads very naturally.
23
+ =end
24
+
25
+
26
+ def as_json(*) to_s; end
27
+
28
+ def to_s
29
+ buf = String.new
30
+ buf << "#{m}"
31
+ ### note - do NOT autoadd minute quote for now
32
+ ## make it an option e.g. to_s( :quote) or such - why? why not?
33
+ ### buf << "'"
34
+ buf << "+#{offset}" if offset
35
+ buf << "/#{secs} secs" if secs
36
+ buf
37
+ end
38
+
39
+ def pretty_print( q )
40
+ q.text( to_s )
41
+ end
42
+
43
+
44
+ include Comparable ## note - auto-adds <, <=, ==, >=, >, and between?
45
+ def <=>(other)
46
+ return nil unless other.is_a?(Minute)
47
+
48
+ ## returns -1/0/1
49
+ res = m <=> other.m
50
+ res = (offset||0) <=> (other.offset||0) if res == 0
51
+ res
52
+ end
53
+ end # class Minute
54
+
55
+
56
+ end # module Model
57
+ end # module Fbtxt
@@ -0,0 +1,35 @@
1
+ module Fbtxt
2
+ module Model
3
+
4
+ class Penalty
5
+
6
+ ## todo
7
+ ## - add attr_reader :team ## note - 1|2 expected - why? why not?
8
+
9
+
10
+ attr_reader :name
11
+ attr_reader :score
12
+ attr_reader :note
13
+
14
+ def initialize( name:, score: nil, note: nil )
15
+ @name = name
16
+ @score = score
17
+ @note = note
18
+ end
19
+
20
+
21
+ ## add alias for name => player - why? why not?
22
+ ## alias_method :player, :name
23
+
24
+
25
+ def as_json(*)
26
+ h = { 'name' => name }
27
+ h['score'] = score if score.is_a?(Array) && !score.empty?
28
+ h['note'] = note if note
29
+
30
+ h
31
+ end
32
+ end ## Penalty
33
+
34
+ end # module Model
35
+ end # module Fbtxt
@@ -0,0 +1,32 @@
1
+
2
+ module Fbtxt
3
+ module Model
4
+
5
+
6
+ class Player
7
+
8
+ attr_reader :name
9
+ ## fix-fix-fix
10
+ ## add pos(ition) e.g. GK,DF,MF,FW
11
+
12
+
13
+ def initialize( name:, captain: false )
14
+ @name = name
15
+ @captain = captain
16
+ end
17
+
18
+ def captain?() @captain; end
19
+
20
+
21
+
22
+ def as_json(*)
23
+ h = { 'name' => name }
24
+ h['captain'] = true if captain?
25
+
26
+ h
27
+ end
28
+ end # class Player
29
+
30
+
31
+ end # module Model
32
+ end # module Fbtxt
@@ -0,0 +1,24 @@
1
+
2
+ module Fbtxt
3
+ module Model
4
+
5
+
6
+ class Referee ## rename to/use Official - why? why not?
7
+ attr_reader :name, :country
8
+
9
+ def initialize( name:, country: nil )
10
+ @name = name
11
+ @country = country
12
+ end
13
+
14
+ def as_json(*)
15
+ h = { 'name' => name }
16
+ h['country'] = country if country
17
+
18
+ h
19
+ end
20
+ end # class Referee
21
+
22
+
23
+ end # module Model
24
+ end # module Fbtxt
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Fbtxt
3
- class MatchTree
3
+ module Model
4
4
 
5
5
 
6
6
  class Round
@@ -30,5 +30,5 @@ class MatchTree
30
30
  end # class Round
31
31
 
32
32
 
33
- end # class MatchTree
33
+ end # module Model
34
34
  end # module Fbtxt