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,146 @@
1
+
2
+ module Fbtxt
3
+ module Model
4
+
5
+ ### note - use inline Score class Match::Score - why? why not?
6
+ ## note - score might internally be an array [2,3] -- now reported (!!)
7
+ ## or hash { ft:, } etc.
8
+
9
+ ## note - score for now might be
10
+ ## 1) array e.g. [1,0] or [] --- now reported (!!)
11
+ ## 2) hash e.g. { ft: [1,0] } etc.
12
+
13
+ ## if node.score.is_a?(Array)
14
+ ## ## assume "undefined" score
15
+ ## score = node.score
16
+ ## else ## (default) assume Hash
17
+ ## # ht = node.score[:ht] || [nil,nil]
18
+ ## # ft = node.score[:ft] || [nil,nil]
19
+ ## # et = node.score[:et] || [nil,nil]
20
+ ## # p = node.score[:p] || [nil,nil]
21
+ ## # values = [*ht, *ft, *et, *p]
22
+ ## # pp values
23
+ ## ## pp node.score
24
+ ## score = node.score
25
+ ## end
26
+ ## end
27
+
28
+
29
+
30
+ class Score
31
+
32
+ def self.build( score )
33
+ if score.nil?
34
+ raise ArgumentError, "Score.build - expected Hash or Array; got nil"
35
+ elsif score.is_a?(Hash)
36
+ new( **score.transform_keys(&:to_sym) )
37
+ elsif score.is_a?(Array)
38
+ new( reported: score )
39
+ else
40
+ raise ArgumentError, "Score.build - expected Hash or Array; got type #{score} #{score.class.name}"
41
+ end
42
+ end
43
+
44
+
45
+
46
+ attr_reader :ht, :ft, :et, :p, :agg,
47
+ :reported
48
+
49
+
50
+ def initialize( ht: nil, ft: nil, et: nil, p: nil,
51
+ agg: nil,
52
+ reported: nil,
53
+ score: nil )
54
+ @ht, @ft, @et, @p = ht, ft, et, p
55
+ @agg = agg
56
+
57
+ ##
58
+ ## note: use reported for "generic" score where
59
+ ## period is not known (might be full-time or aet)
60
+ ## or undefined e.g. for abandoned or awarded (administered) score
61
+ @reported = reported
62
+
63
+ if score
64
+ puts "!! DEPRECATED - score got score keyword (use reported):"
65
+ pp [ ht, ft, et, p, agg, reported, score]
66
+ @reported = score
67
+ end
68
+ end
69
+
70
+ ###
71
+ # only know reported "vanilla" score (might be full-time? extra-time? etc.)
72
+ def reported?() @reported.is_a?( Array ) && @reported.size == 2; end
73
+
74
+
75
+ def ft?() @ft.is_a?( Array ) && @ft.size == 2; end
76
+ def et?() @et.is_a?( Array ) && @et.size == 2; end
77
+ def p?() @p.is_a?( Array ) && @p.size == 2; end
78
+
79
+
80
+
81
+ def to_s
82
+ if @reported
83
+ ## check for ary empty - why? why not?
84
+ "#{@reported[0]}-#{@reported[1]}"
85
+ else
86
+ if @et && @p
87
+ if @ft && @ht
88
+ "#{@et[0]}-#{@et[1]} a.e.t." +
89
+ " (#{@ft[0]}-#{@ft[1]}, #{@ht[0]}-#{@ht[1]}), " +
90
+ "#{@p[0]}-#{@p[1]} pen."
91
+ elsif @ft
92
+ "#{@et[0]}-#{@et[1]} a.e.t." +
93
+ " (#{@ft[0]}-#{@ft[1]}), " +
94
+ "#{@p[0]}-#{@p[1]} pen."
95
+ else
96
+ "#{@et[0]}-#{@et[1]} a.e.t., " +
97
+ "#{@p[0]}-#{@p[1]} pen."
98
+ end
99
+ elsif @et && @p.nil?
100
+ if @ft && @ht
101
+ "#{@et[0]}-#{@et[1]} a.e.t."+
102
+ " (#{@ft[0]}-#{@ft[1]}, #{@ht[0]}-#{@ht[1]})"
103
+ elsif @ft
104
+ "#{@et[0]}-#{@et[1]} a.e.t."+
105
+ " (#{@ft[0]}-#{@ft[1]})"
106
+ else
107
+ "#{@et[0]}-#{@et[1]} a.e.t."
108
+ end
109
+ elsif @ft && @et.nil? && @p.nil?
110
+ if @ht
111
+ "#{@ft[0]}-#{@ft[1]} (#{@ht[0]}-#{@ht[1]})"
112
+ else
113
+ "#{@ft[0]}-#{@ft[1]}"
114
+ end
115
+ else
116
+ ## raise error/warn - about unknown (or empty) score type - why? why not?
117
+ ''
118
+ end
119
+ end
120
+ end
121
+
122
+
123
+
124
+ def as_json(*)
125
+ if @reported
126
+ ## return array only - why? why not?
127
+ ## or use hash with reported or _ - key - why? why not?
128
+ @reported
129
+ else
130
+ h = {}
131
+ h['ht'] = @ht if @ht
132
+ h['ft'] = @ft if @ft
133
+ h['et'] = @et if @et
134
+ h['p'] = @p if @p
135
+ h['agg'] = @agg if @agg
136
+ h
137
+ end
138
+ end
139
+
140
+
141
+
142
+ end # class Score
143
+
144
+
145
+ end # module Model
146
+ end # module Fbtxt
@@ -4,7 +4,7 @@ module Fbtxt
4
4
  module Document
5
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
6
6
  MINOR = 9
7
- PATCH = 0
7
+ PATCH = 1
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
@@ -9,25 +9,52 @@ require 'season-formats'
9
9
  ## our own code
10
10
  require_relative 'document/version'
11
11
 
12
+ ###########
13
+ ## ("inline") structs
14
+ require_relative 'document/models/match'
15
+ require_relative 'document/models/goal'
16
+ require_relative 'document/models/round'
17
+ require_relative 'document/models/group'
18
+ require_relative 'document/models/player'
19
+ require_relative 'document/models/referee'
20
+ require_relative 'document/models/lineup'
21
+ require_relative 'document/models/penalty'
22
+ require_relative 'document/models/event' ## cards, sub(stitution)s
23
+ require_relative 'document/models/minute'
24
+ require_relative 'document/models/score'
25
+
26
+
27
+
28
+ ###
29
+ # add Models alias for Model
30
+ ## e.g. include Models (instead of include Model) -- keep - why? why not?
31
+ module Fbtxt
32
+ Models = Model
33
+ end
34
+
35
+
12
36
  ## match & league machinery
13
37
  require_relative 'document/match_tree'
14
38
  require_relative 'document/match_tree-helpers'
15
39
 
16
- require_relative 'document/match_tree/match' ## ("inline") structs
17
- require_relative 'document/match_tree/goal'
18
- require_relative 'document/match_tree/round'
19
- require_relative 'document/match_tree/group'
20
-
21
40
  require_relative 'document/match_tree_on/on_group_def'
22
41
  require_relative 'document/match_tree_on/on_round_def'
23
42
  require_relative 'document/match_tree_on/on_round_outline'
24
43
  require_relative 'document/match_tree_on/on_date_header'
25
44
  require_relative 'document/match_tree_on/on_match_line'
45
+ require_relative 'document/match_tree_on/on_match_bye'
26
46
  require_relative 'document/match_tree_on/on_goal_line'
47
+ require_relative 'document/match_tree_on/on_penalties'
48
+ require_relative 'document/match_tree_on/on_lineup_line'
49
+ require_relative 'document/match_tree_on/on_cards_line'
50
+ require_relative 'document/match_tree_on/on_referee_line'
51
+
52
+
53
+ require_relative 'document/document' ## e.g. use Fbtxt::Document.parse/read/etc.
27
54
 
28
55
 
29
- require_relative 'document/quick_match_reader'
30
56
 
57
+ require_relative 'document/export_utils' ## e.g. (batch) genjson (via config)
31
58
 
32
59
 
33
60
  puts Fbtxt::Module::Document.banner # say hello
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbtxt-document
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-07 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fbtxt-parser
@@ -87,19 +87,32 @@ files:
87
87
  - README.md
88
88
  - Rakefile
89
89
  - lib/fbtxt/document.rb
90
+ - lib/fbtxt/document/document.rb
91
+ - lib/fbtxt/document/export_utils.rb
90
92
  - lib/fbtxt/document/match_tree-helpers.rb
91
93
  - lib/fbtxt/document/match_tree.rb
92
- - lib/fbtxt/document/match_tree/goal.rb
93
- - lib/fbtxt/document/match_tree/group.rb
94
- - lib/fbtxt/document/match_tree/match.rb
95
- - lib/fbtxt/document/match_tree/round.rb
94
+ - lib/fbtxt/document/match_tree_on/on_cards_line.rb
96
95
  - lib/fbtxt/document/match_tree_on/on_date_header.rb
97
96
  - lib/fbtxt/document/match_tree_on/on_goal_line.rb
98
97
  - lib/fbtxt/document/match_tree_on/on_group_def.rb
98
+ - lib/fbtxt/document/match_tree_on/on_lineup_line.rb
99
+ - lib/fbtxt/document/match_tree_on/on_match_bye.rb
99
100
  - lib/fbtxt/document/match_tree_on/on_match_line.rb
101
+ - lib/fbtxt/document/match_tree_on/on_penalties.rb
102
+ - lib/fbtxt/document/match_tree_on/on_referee_line.rb
100
103
  - lib/fbtxt/document/match_tree_on/on_round_def.rb
101
104
  - lib/fbtxt/document/match_tree_on/on_round_outline.rb
102
- - lib/fbtxt/document/quick_match_reader.rb
105
+ - lib/fbtxt/document/models/event.rb
106
+ - lib/fbtxt/document/models/goal.rb
107
+ - lib/fbtxt/document/models/group.rb
108
+ - lib/fbtxt/document/models/lineup.rb
109
+ - lib/fbtxt/document/models/match.rb
110
+ - lib/fbtxt/document/models/minute.rb
111
+ - lib/fbtxt/document/models/penalty.rb
112
+ - lib/fbtxt/document/models/player.rb
113
+ - lib/fbtxt/document/models/referee.rb
114
+ - lib/fbtxt/document/models/round.rb
115
+ - lib/fbtxt/document/models/score.rb
103
116
  - lib/fbtxt/document/version.rb
104
117
  homepage: https://github.com/sportdb/sport.db
105
118
  licenses:
@@ -1,67 +0,0 @@
1
- module Fbtxt
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( q )
51
- q.group( 4, '<Goal', '>') do ## group( indent, open, close)
52
- buf = String.new
53
- buf << " #{@player} #{@minute}"
54
- buf << "+#{@offset}" if @offset && @offset > 0
55
- buf << "'"
56
- q.text( buf )
57
-
58
- q.text( "(og)" ) if @owngoal
59
- q.text( "(p)" ) if @penalty
60
- q.text( " for #{@team}" ) ### team 1 or 2 - use home/away
61
- end
62
- end
63
- end # class Goal
64
-
65
-
66
- end # class MatchTree
67
- end # module Fbtxt
@@ -1,249 +0,0 @@
1
- ##
2
- # move (simpler) struct version inline to MatchTree for now
3
- #
4
- module Fbtxt
5
- class MatchTree
6
-
7
-
8
-
9
- class Match
10
-
11
- ### note - use inline Score class Match::Score - why? why not?
12
- ## note - score might internally be an array [2,3]
13
- ## or hash { ft:, } etc.
14
-
15
- ## note - score for now might be
16
- ## 1) array e.g. [1,0] or []
17
- ## 2) hash e.g. { ft: [1,0] } etc.
18
-
19
- attr_reader :num,
20
- :date,
21
- :time,
22
- :time_local,
23
- :team1, :team2, ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
24
- :score,
25
- :round, ## todo/fix: use round_num or similar - for compat with db activerecord version? why? why not?
26
- :group,
27
- :status, ## e.g. replay, cancelled, awarded, abadoned, postponed, etc.
28
- :ground, ## (optional) add as text line for now (incl. city, timezone etc.)
29
- :att ## (optional) attendance as (integer) number
30
-
31
-
32
- attr_accessor :goals ## todo/fix: make goals like all other attribs!!
33
-
34
- def initialize( **kwargs )
35
- @score = []
36
- ## @score1, @score2 = [nil,nil] ## full time
37
- ## @score1i, @score2i = [nil,nil] ## half time (first (i) part)
38
- ## @score1et, @score2et = [nil,nil] ## extra time
39
- ## @score1p, @score2p = [nil,nil] ## penalty
40
- ## @score1agg, @score2agg = [nil,nil] ## full time (all legs) aggregated
41
-
42
-
43
- update( **kwargs ) unless kwargs.empty?
44
- end
45
-
46
-
47
- def update( **kwargs )
48
- @num = kwargs[:num] if kwargs.has_key?( :num )
49
-
50
- ## note: check with has_key? because value might be nil!!!
51
- @date = kwargs[:date] if kwargs.has_key?( :date )
52
- @time = kwargs[:time] if kwargs.has_key?( :time )
53
- @time_local = kwargs[:time_local] if kwargs.has_key?( :time_local )
54
-
55
- ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
56
- @team1 = kwargs[:team1] if kwargs.has_key?( :team1 )
57
- @team2 = kwargs[:team2] if kwargs.has_key?( :team2 )
58
-
59
- ## note: round is a string!!! e.g. '1', '2' for matchday or 'Final', 'Semi-final', etc.
60
- ## todo: use to_s - why? why not?
61
- @round = kwargs[:round] if kwargs.has_key?( :round )
62
- @group = kwargs[:group] if kwargs.has_key?( :group )
63
- @status = kwargs[:status] if kwargs.has_key?( :status )
64
-
65
- @ground = kwargs[:ground] if kwargs.has_key?( :ground )
66
- @att = kwargs[:att] if kwargs.has_key?( :att )
67
-
68
-
69
- if kwargs.has_key?( :score ) ## check all-in-one score struct for convenience!!!
70
- score = kwargs[:score]
71
-
72
- if score.nil? ## reset all score attribs to nil!!
73
- @score = [] ## [nil,nil]
74
- else
75
- ## check if is array - assume "generic" score e.g. 3-2
76
- ## that is, not known if full-time, after extra-time etc.
77
- if score.is_a?( Array )
78
- @score = score ## e.g. [3,2]
79
- else ## assume hash
80
- @score = score
81
- # @score1, @score2 = score[:ft] || []
82
- # @score1i, @score2i = score[:ht] || []
83
- # @score1et, @score2et = score[:et] || []
84
- # @score1p, @score2p = score[:p] || score[:pen] || []
85
- # @score1agg, @score2agg = score[:agg] || []
86
- end
87
- end
88
- end
89
- # @score[:ht] = kwargs[:score_ht] if kwargs.has_key?( :score_ht )
90
- # @score[:et] = kwargs[:score_et] if kwargs.has_key?( :score_et )
91
- # @score[:p] = kwargs[:score_p] if kwargs.has_key?( :score_p )
92
- # @score[:agg] = kwargs[:score_agg] if kwargs.has_key?( :score_agg )
93
-
94
- ## note: (always) (auto-)convert scores to integers
95
- # @score1 = @score1.to_i(10) if @score1
96
- # @score1i = @score1i.to_i(10) if @score1i
97
- # @score1et = @score1et.to_i(10) if @score1et
98
- # @score1p = @score1p.to_i(10) if @score1p
99
- # @score1agg = @score1agg.to_i(10) if @score1agg
100
-
101
- # @score2 = @score2.to_i(10) if @score2
102
- # @score2i = @score2i.to_i(10) if @score2i
103
- # @score2et = @score2et.to_i(10) if @score2et
104
- # @score2p = @score2p.to_i(10) if @score2p
105
- # @score2agg = @score2agg.to_i(10) if @score2agg
106
-
107
- ## todo/fix:
108
- ## gr-greece/2014-15/G1.csv:
109
- ## G1,10/05/15,Niki Volos,OFI,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
110
- ##
111
-
112
- ## for now score1 and score2 must be present
113
- ## if @score1.nil? || @score2.nil?
114
- ## puts "** WARN: missing scores for match:"
115
- ## pp kwargs
116
- ## ## exit 1
117
- ## end
118
-
119
-
120
- self ## note - MUST return self for chaining
121
- end
122
-
123
-
124
- ####
125
- ## deprecated - use score.to_s and friends - why? why not?
126
- # def score_str # pretty print (full time) scores; convenience method
127
- # "#{@score1}-#{@score2}"
128
- # end
129
-
130
- # def scorei_str # pretty print (half time) scores; convenience method
131
- # "#{@score1i}-#{@score2i}"
132
- # end
133
-
134
-
135
- def as_json
136
- #####
137
- ## note - use string keys (NOT symbol for data keys)
138
- ## for easier json compatibility
139
- data = {}
140
-
141
- ## check round
142
- if @round
143
- data['round'] = if round.is_a?( Integer )
144
- "Matchday #{@round}"
145
- else ## assume string
146
- @round
147
- end
148
- end
149
-
150
-
151
- data['num'] = @num if @num
152
-
153
-
154
- if @date
155
- ## assume 2020-09-19 date format!!
156
- data['date'] = @date.is_a?( String ) ? @date : @date.strftime('%Y-%m-%d')
157
-
158
- data['time'] = @time if @time
159
- data['time_local'] = @time_local if @time_local
160
- end
161
-
162
-
163
- data['team1'] = @team1.is_a?( String ) ? @team1 : @team1.name
164
-
165
- ## note - for match status bye team2 is nil!!!
166
- ## e.g. Queen's Park bye
167
- ## Wanderers bye
168
- ## todo/check - keep bye as a match - why? why not?
169
- ## has no date/time & venue & score etc.
170
- if @team2
171
- data['team2'] = @team2.is_a?( String ) ? @team2 : @team2.name
172
- end
173
-
174
- ## note - score might be
175
- ## 1) array e.g. [0,1]
176
- ## 2) hash e.g. { ft: [0,1] } etc.
177
- ## note - w/o (walkout) do NOT add empty score
178
- if @score.is_a?(Hash)
179
- # note: make sure hash keys are always strings
180
- data['score'] = @score.transform_keys(&:to_s)
181
- elsif @score.is_a?(Array)
182
- ## note:
183
- ## for now always assume full-time (ft)
184
- ## in future check for score note or such
185
- ## to use "plain" array or such - why? why not?
186
- ## data['score'] = { 'ft' => @score } if !@score.empty?
187
-
188
- data['score'] = @score if !@score.empty?
189
- end
190
-
191
-
192
- ## data['score']['ht'] = [@score1i, @score2i] if @score1i && @score2i
193
- ## data['score']['ft'] = [@score1, @score2] if @score1 && @score2
194
- ## data['score']['et'] = [@score1et, @score2et] if @score1et && @score2et
195
- ## data['score']['p'] = [@score1p, @score2p] if @score1p && @score2p
196
-
197
-
198
-
199
- ### check for goals
200
- if @goals && @goals.size > 0
201
- data['goals1'] = []
202
- data['goals2'] = []
203
-
204
- @goals.each do |goal|
205
- node = {}
206
- node['name'] = goal.player
207
-
208
- ## note - use a string for minutes for now
209
- ## allows e.g. 45+2 etc. too
210
- minute_str = "#{goal.minute}"
211
- minute_str += "+#{goal.offset}" if goal.offset
212
-
213
- node['minute'] = minute_str
214
-
215
- node['owngoal'] = true if goal.owngoal
216
- node['penalty'] = true if goal.penalty
217
-
218
- if goal.team == 1
219
- data['goals1'] << node
220
- else ## assume 2
221
- data['goals2'] << node
222
- end
223
- end # each goal
224
- end
225
-
226
-
227
- data['status'] = @status if @status
228
-
229
- data['group'] = @group if @group
230
-
231
- if @ground
232
- ## note: might be array of string e.g. ['Wembley', 'London']
233
- ##
234
- ## todo/check - auto-join to string - why? why not?
235
- ## e.g. ['Wembley', 'London']
236
- ## to 'Wembley, London'
237
- ## note - auto-join geo tree for now
238
- data['ground'] = @ground.join(', ')
239
- end
240
-
241
- data['attendance'] = @att if @att
242
- data
243
- end
244
- end # class Match
245
-
246
-
247
-
248
- end # class MatchTree
249
- end # module Fbtxt