sportdb-writers 0.4.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8617a863daaeb1b9d99c74068d58e762086ba329a423dee7effce66659b28e33
4
- data.tar.gz: 920417fb198af92e662102d356cfae8edb038fdeb9f7659d6538b942ed6f2835
3
+ metadata.gz: fd14b82e447eaf5824ab5b9636b9a91decee9d0962bb4c1b43d60e38c48e8a7e
4
+ data.tar.gz: 1b93a17a72cff8cab52b5f40929d57bbb3fa8c0d84a51ee0dc6b33b34028c4ae
5
5
  SHA512:
6
- metadata.gz: 1e179dfaa2ad55f64f690308a83af8a0861056050faa7e1d0e5645d7098c54f090ffdc24a8dea30d903ce4b72c2860a3a3741b8f954ee9f343a4f27d62df8297
7
- data.tar.gz: 00340a7bfb452dbf504e15346f6ecdd63757853dd2144a868bc77e4742b4f8f6efe1c98c982c6a449caaf20d5cd4883bdc99317e949caa80f72e245fef62b5c0
6
+ metadata.gz: ef84ebdd48f0751fab889ec66f6a92a22827f192f4ccd9bf52cd6cec0c6c35578dda52c02538964333515f3d006cb2bd68ad63501ffc01218be88cf7f20ee54f
7
+ data.tar.gz: d0313c242c59d62aa6b9874acd0e16a35142e1945d6100d8d8a021544266ca548c6b9819856118781f58f72b2d84a9137f1d1c1b5ea498606961bc2de3bbe57c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,4 @@
1
- ### 0.4.2
1
+ ### 0.5.0
2
2
  ### 0.0.1 / 2020-11-15
3
3
 
4
4
  * Everything is new. First release.
5
-
data/Manifest.txt CHANGED
@@ -3,6 +3,8 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  lib/sportdb/writers.rb
6
- lib/sportdb/writers/goals.rb
6
+ lib/sportdb/writers/build_goals.rb
7
+ lib/sportdb/writers/build_stats.rb
8
+ lib/sportdb/writers/rounds.rb
7
9
  lib/sportdb/writers/txt_writer.rb
8
10
  lib/sportdb/writers/version.rb
@@ -55,4 +55,50 @@ def self.merge_goals( matches, goals )
55
55
  end
56
56
 
57
57
 
58
+
59
+ def self.build_goals( goals )
60
+ ## todo/fix: for now assumes always minutes (without offset) - add offset support
61
+
62
+ ## note: "fold" multiple goals by players
63
+ team1_goals = {}
64
+ team2_goals = {}
65
+ goals.each do |goal|
66
+ team_goals = goal.team == 1 ? team1_goals : team2_goals
67
+ player_goals = team_goals[ goal.player ] ||= []
68
+ player_goals << goal
69
+ end
70
+
71
+ buf = String.new
72
+ if team1_goals.size > 0
73
+ buf << build_goals_for_team( team1_goals )
74
+ end
75
+
76
+ ## note: only add a separator (;) if BOTH teams have goal scores
77
+ if team1_goals.size > 0 && team2_goals.size > 0
78
+ buf << '; '
79
+ end
80
+
81
+ if team2_goals.size > 0
82
+ buf << build_goals_for_team( team2_goals )
83
+ end
84
+ buf
85
+ end
86
+
87
+
88
+ def self.build_goals_for_team( team_goals )
89
+ buf = String.new
90
+ team_goals.each_with_index do |(player_name, goals),i|
91
+ buf << ' ' if i > 0
92
+ buf << "#{player_name} "
93
+ buf << goals.map do |goal|
94
+ str = "#{goal.minute}'"
95
+ str << "(og)" if goal.owngoal?
96
+ str << "(p)" if goal.penalty?
97
+ str
98
+ end.join( ', ' )
99
+ end
100
+ buf
101
+ end
102
+
103
+
58
104
  end # module Writer
@@ -0,0 +1,95 @@
1
+ module SportDb
2
+ class TxtMatchWriter
3
+
4
+
5
+
6
+ ## helper - to calculate match stats e.g.
7
+ ## how many stages, start and end dates, etc.
8
+ def self._calc_stats( matches )
9
+ ### check for stages & stats
10
+ stats = { 'stage' => Hash.new(0),
11
+ 'date' => { 'start_date' => nil,
12
+ 'end_date' => nil, },
13
+ 'teams' => Hash.new(0),
14
+ 'matches' => 0,
15
+ 'use_stages' => false,
16
+ }
17
+
18
+ ## add matches played stats too??
19
+ ## for now only simply count used
20
+ stats['matches'] = matches.size
21
+
22
+ matches.each do |match|
23
+ stage = match.stage
24
+ stage = 'Regular Season' if stage.nil? || stage.empty?
25
+ stats['stage'][ stage ] += 1
26
+
27
+ ## note - date for now optional (not required)!!!!
28
+ ## only teams for match
29
+ if match.date
30
+ ## todo/fix - norm date (parse as Date)
31
+ ## check format etc.
32
+ date = if match.date.is_a?( String )
33
+ Date.strptime( match.date, '%Y-%m-%d' )
34
+ else ## assume it's already a date (object)
35
+ match.date
36
+ end
37
+ stats['date']['start_date'] ||= date
38
+ stats['date']['end_date'] ||= date
39
+
40
+ stats['date']['start_date'] = date if date < stats['date']['start_date']
41
+ stats['date']['end_date'] = date if date > stats['date']['end_date']
42
+ end
43
+
44
+ [match.team1, match.team2].each do |team|
45
+ stats['teams'][ team ] += 1 if team && !['N.N.'].include?( team )
46
+ end
47
+ end
48
+
49
+ ## add & update use_stage flag
50
+ stats['use_stages'] = if stats['stage'].size >= 2 ||
51
+ (stats['stage'].size == 1 &&
52
+ stats['stage'].keys[0] != 'Regular Season')
53
+ true
54
+ else
55
+ false
56
+ end
57
+ stats
58
+ end
59
+
60
+ def self._build_stats( stats )
61
+ ### add comment header
62
+ buf = String.new
63
+ # e.g. 13 April – 25 September 2024
64
+ # or 16 August 2024 – 25 May 2025
65
+ ## note - date is optional!!
66
+ if stats['date']['start_date']
67
+ buf << "# Date "
68
+ start_date = stats['date']['start_date']
69
+ end_date = stats['date']['end_date']
70
+ if start_date.year != end_date.year
71
+ buf << "#{start_date.strftime('%a %b %-d %Y')} - #{end_date.strftime('%a %b %-d %Y')}"
72
+ else
73
+ buf << "#{start_date.strftime('%a %b %-d')} - #{end_date.strftime('%a %b %-d %Y')}"
74
+ end
75
+ buf << " (#{end_date.jd-start_date.jd}d)" ## add days
76
+ buf << "\n"
77
+ end
78
+
79
+ buf << "# Teams #{stats['teams'].size}\n"
80
+ buf << "# Matches #{stats['matches']}\n"
81
+
82
+ if stats['use_stages']
83
+ buf << "# Stages "
84
+ stages = stats['stage'].map { |name,count| "#{name} (#{count})" }.join( ' ' )
85
+ buf << stages
86
+ buf << "\n"
87
+ end
88
+
89
+ buf
90
+ end
91
+
92
+
93
+
94
+ end # class TxtMatchWriter
95
+ end # module SportDb
@@ -0,0 +1,26 @@
1
+ module SportDb
2
+ class TxtMatchWriter
3
+
4
+
5
+
6
+ ## translate from lang x (german, etc) to english
7
+ ROUND_TRANSLATIONS = {
8
+ # de/german
9
+ '1. Runde' => 'Round 1',
10
+ '2. Runde' => 'Round 2',
11
+ 'Achtelfinale' => 'Round of 16',
12
+ 'Viertelfinale' => 'Quarterfinals',
13
+ 'Halbfinale' => 'Semifinals',
14
+ 'Finale' => 'Final',
15
+
16
+ 'Gruppe A' => 'Group A',
17
+ 'Gruppe B' => 'Group B',
18
+ 'Gruppe C' => 'Group C',
19
+ 'Gruppe D' => 'Group D',
20
+ 'Gruppe E' => 'Group E',
21
+ 'Gruppe F' => 'Group F',
22
+ }
23
+
24
+
25
+ end # class TxtMatchWriter
26
+ end # module SportDb
@@ -2,17 +2,6 @@ module SportDb
2
2
  class TxtMatchWriter
3
3
 
4
4
 
5
- ## translate from lang x (german, etc) to english
6
- ROUND_TRANSLATIONS = {
7
- # de/german
8
- '1. Runde' => 'Round 1',
9
- '2. Runde' => 'Round 2',
10
- 'Achtelfinale' => 'Round of 16',
11
- 'Viertelfinale' => 'Quarterfinals',
12
- 'Halbfinale' => 'Semifinals',
13
- 'Finale' => 'Final',
14
- }
15
-
16
5
 
17
6
  ## note: build returns buf - an (in-memory) string buf(fer)
18
7
  def self.build( matches, rounds: true )
@@ -21,105 +10,18 @@ def self.build( matches, rounds: true )
21
10
 
22
11
 
23
12
  ### check for stages & stats
24
- stats = { 'stage' => Hash.new(0),
25
- 'date' => { 'start_date' => nil,
26
- 'end_date' => nil, },
27
- 'teams' => Hash.new(0),
28
- }
29
-
30
- ## add matches played stats too??
31
-
32
- matches.each do |match|
33
- stage = match.stage
34
- stage = 'Regular Season' if stage.nil? || stage.empty?
35
- stats['stage'][ stage ] += 1
36
-
37
- if match.date
38
-
39
- ## todo/fix - norm date (parse as Date)
40
- ## check format etc.
41
- date = if match.date.is_a?( String )
42
- Date.strptime( match.date, '%Y-%m-%d' )
43
- else ## assume it's already a date (object)
44
- match.date
45
- end
46
- stats['date']['start_date'] ||= date
47
- stats['date']['end_date'] ||= date
48
-
49
- stats['date']['start_date'] = date if date < stats['date']['start_date']
50
- stats['date']['end_date'] = date if date > stats['date']['end_date']
51
- end
13
+ stats = _calc_stats( matches )
52
14
 
53
- [match.team1, match.team2].each do |team|
54
- stats['teams'][ team ] += 1 if team && !['N.N.'].include?( team )
55
- end
56
- end
57
15
 
58
- use_stages = if stats['stage'].size >= 2 ||
59
- (stats['stage'].size == 1 &&
60
- stats['stage'].keys[0] != 'Regular Season')
61
- true
62
- else
63
- false
64
- end
16
+ buf = String.new
65
17
 
18
+ ### add comment header
19
+ buf << _build_stats( stats )
20
+ buf << "\n\n"
66
21
 
67
- ### add comment header
68
- buf = String.new
69
- # e.g. 13 April – 25 September 2024
70
- # or 16 August 2024 – 25 May 2025
71
- ## note - date is optional!!
72
- if stats['date']['start_date']
73
- buf << "# Date "
74
- start_date = stats['date']['start_date']
75
- end_date = stats['date']['end_date']
76
- if start_date.year != end_date.year
77
- buf << "#{start_date.strftime('%a %b/%-d %Y')} - #{end_date.strftime('%a %b/%-d %Y')}"
78
- else
79
- buf << "#{start_date.strftime('%a %b/%-d')} - #{end_date.strftime('%a %b/%-d %Y')}"
80
- end
81
- buf << " (#{end_date.jd-start_date.jd}d)" ## add days
82
- buf << "\n"
83
- end
84
-
85
- buf << "# Teams #{stats['teams'].size}\n"
86
- buf << "# Matches #{matches.size}\n"
87
-
88
- if use_stages
89
- buf << "# Stages "
90
- stages = stats['stage'].map { |name,count| "#{name} (#{count})" }.join( ' ' )
91
- buf << stages
92
- buf << "\n"
93
- end
94
- buf << "\n\n"
95
-
96
-
97
- if use_stages
98
- ## split matches by stage
99
- matches_by_stage = {}
100
- matches.each do |match|
101
- stage = match.stage || ''
102
- matches_by_stage[stage] ||= []
103
- matches_by_stage[stage] << match
104
- end
105
-
106
- ## todo/fix
107
- ## note - empty stage must go first!!!!
108
- matches_by_stage.each_with_index do |(name, matches),i|
109
- buf << "\n" if i != 0 # add extra new line (if not first stage)
110
- if name.empty?
111
- buf << "# Regular Season\n" ## empty stage
112
- else
113
- buf << "== #{name}\n"
114
- end
115
- buf += _build_batch( matches, rounds: rounds )
116
- buf << "\n" if i+1 != matches_by_stage.size
117
- end
118
- buf
119
- else
120
- buf += _build_batch( matches, rounds: rounds )
121
- buf
122
- end
22
+ buf << _build_batch( matches, rounds: rounds )
23
+
24
+ buf
123
25
  end
124
26
 
125
27
 
@@ -127,40 +29,62 @@ def self._build_batch( matches, rounds: true )
127
29
  ## note: make sure rounds is a bool, that is, true or false (do NOT pass in strings etc.)
128
30
  raise ArgumentError, "rounds flag - bool expected; got: #{rounds.inspect}" unless rounds.is_a?( TrueClass ) || rounds.is_a?( FalseClass )
129
31
 
130
- buf = String.new
131
-
132
32
  last_round = nil
33
+ last_year = nil
133
34
  last_date = nil
134
35
  last_time = nil
135
36
 
136
37
 
38
+ buf = String.new
39
+
40
+
137
41
  matches.each_with_index do |match,i|
138
42
 
139
43
  ## note: make rounds optional (set rounds flag to false to turn off)
140
44
  if rounds
141
- if match.round != last_round
45
+ ## build round string
46
+ round = if match.round
47
+ if match.round.is_a?( Integer ) ||
48
+ match.round =~ /^[0-9]+$/ ## all numbers/digits
49
+ ## default "class format
50
+ ## e.g. Runde 1, Spieltag 1, Matchday 1, Week 1
51
+ "Matchday #{match.round}"
52
+ else ## use as is from match
53
+ ## note: for now assume english names
54
+ (ROUND_TRANSLATIONS[match.round] || match.round)
55
+ end
56
+ else
57
+ nil
58
+ end
59
+
60
+ ## note - only stage present is possible
61
+ ## if stage present - (auto-)add upfront
62
+ if match.stage
63
+ if ['Regular', 'Regular stage'].include?(match.stage)
64
+ ## skip stage
65
+ else
66
+ round = round ? "#{match.stage}, #{round}" : "#{match.stage}"
67
+ end
68
+ end
69
+
70
+
71
+ if round.nil?
72
+ puts "!! ERROR - match with round nil (no round and no stage)"
73
+ pp match
74
+ exit 1
75
+ end
76
+
77
+
78
+
79
+ if round != last_round
142
80
  buf << (i == 0 ? "\n" : "\n\n") ## start with single empty line
143
- if match.round.is_a?( Integer ) ||
144
- match.round =~ /^[0-9]+$/ ## all numbers/digits
145
- ## default "class format
146
- ## e.g. Runde 1, Spieltag 1, Matchday 1, Week 1
147
- buf << "Matchday #{match.round}"
148
- else ## use as is from match
149
- ## note: for now assume english names
150
- if match.round.nil?
151
- ## warn
152
- puts "!! ERROR - match with round nil?"
153
- pp match
154
- exit 1
155
- end
81
+ buf << "▪ #{round}\n"
156
82
 
157
- buf << (ROUND_TRANSLATIONS[match.round] || match.round)
158
- end
159
83
  ## note - reset last_date & last_time on every new round header
160
- last_date = nil
161
- last_time = nil
162
-
163
- buf << "\n"
84
+ ## kind of starting "new scope" (no date/time inheritance)
85
+ last_date = nil
86
+ last_time = nil
87
+ last_round = round
164
88
  end
165
89
  end
166
90
 
@@ -182,15 +106,23 @@ def self._build_batch( matches, rounds: true )
182
106
  date_yyyymmdd = date ? date.strftime( '%Y-%m-%d' ) : nil
183
107
 
184
108
  ## note: time is OPTIONAL for now
185
- ## note: use 17.00 and NOT 17:00 for now
186
- time_hhmm = time ? time.strftime( '%H.%M' ) : nil
109
+ ## note: use 17:00 always as time format (do NOT use 17.00!!!)
110
+ time_hhmm = time ? time.strftime( '%H:%M' ) : nil
187
111
 
188
112
 
189
113
  if date_yyyymmdd
190
114
  if date_yyyymmdd != last_date
191
115
  ## note: add an extra leading blank line (if no round headings printed)
192
116
  buf << "\n" unless rounds
193
- buf << "[#{date.strftime( '%a %b/%-d' )}]\n"
117
+
118
+ buf << " "
119
+ ## note: only add year if different for last date header
120
+ buf << if (date ? date.year : nil) != last_year
121
+ "#{date.strftime( '%a %b %-d %Y' )}"
122
+ else
123
+ "#{date.strftime( '%a %b %-d' )}"
124
+ end
125
+ buf << "\n"
194
126
  last_time = nil
195
127
  end
196
128
  end
@@ -206,22 +138,26 @@ def self._build_batch( matches, rounds: true )
206
138
 
207
139
  if time
208
140
  if last_time != time_hhmm
209
- line << "%5s" % time_hhmm
141
+ line << " %5s" % time_hhmm
210
142
  else
211
- line << ' '
143
+ line << (' ' *7)
212
144
  end
213
145
  line << ' '
214
146
  else
215
- line << ' '
147
+ line << (' ' *9)
216
148
  end
217
149
 
218
150
  line << "%-23s" % team1 ## note: use %-s for left-align
219
-
220
- ## note: separate by at least two spaces for now
221
- line << " #{match.score.to_s( lang: 'en' )} "
222
-
151
+ line << " v "
223
152
  line << "%-23s" % team2
224
153
 
154
+ ## note - only print if score is available
155
+ ## returns - for not available for now!!!!
156
+ score = match.score.to_s( lang: 'en' )
157
+ if score != '-'
158
+ ## note: separate by at least two spaces for now
159
+ line << " #{score} "
160
+ end
225
161
 
226
162
  if match.status
227
163
  line << ' '
@@ -238,9 +174,10 @@ def self._build_batch( matches, rounds: true )
238
174
  line << '[postponed]'
239
175
  ## was -- note: add NOTHING for postponed for now
240
176
  else
241
- puts "!! WARN - unknown match status >#{match.status}<:"
177
+ puts "!! ERROR - unknown match status >#{match.status}<:"
242
178
  pp match
243
179
  line << "[#{match.status.downcase}]" ## print "literal" downcased for now
180
+ exit 1
244
181
  end
245
182
  end
246
183
 
@@ -251,63 +188,16 @@ def self._build_batch( matches, rounds: true )
251
188
  if match.goals
252
189
  buf << ' ' # 4 space indent
253
190
  buf << ' ' if time # 7 (5+2) space indent (for hour e.g. 17.30)
254
- buf << "[#{build_goals(match.goals)}]"
191
+ buf << "(#{build_goals(match.goals)})"
255
192
  buf << "\n"
256
193
  end
257
194
 
258
-
259
- last_round = match.round
195
+ last_year = date ? date.year : nil
260
196
  last_date = date_yyyymmdd
261
197
  last_time = time_hhmm
262
198
  end
263
199
  buf
264
200
  end
265
201
 
266
-
267
- def self.build_goals( goals )
268
- ## todo/fix: for now assumes always minutes (without offset) - add offset support
269
-
270
- ## note: "fold" multiple goals by players
271
- team1_goals = {}
272
- team2_goals = {}
273
- goals.each do |goal|
274
- team_goals = goal.team == 1 ? team1_goals : team2_goals
275
- player_goals = team_goals[ goal.player ] ||= []
276
- player_goals << goal
277
- end
278
-
279
- buf = String.new
280
- if team1_goals.size > 0
281
- buf << build_goals_for_team( team1_goals )
282
- end
283
-
284
- ## note: only add a separator (;) if BOTH teams have goal scores
285
- if team1_goals.size > 0 && team2_goals.size > 0
286
- buf << '; '
287
- end
288
-
289
- if team2_goals.size > 0
290
- buf << build_goals_for_team( team2_goals )
291
- end
292
- buf
293
- end
294
-
295
-
296
- def self.build_goals_for_team( team_goals )
297
- buf = String.new
298
- team_goals.each_with_index do |(player_name, goals),i|
299
- buf << ' ' if i > 0
300
- buf << "#{player_name} "
301
- buf << goals.map do |goal|
302
- str = "#{goal.minute}'"
303
- str << " (o.g.)" if goal.owngoal?
304
- str << " (pen.)" if goal.penalty?
305
- str
306
- end.join( ', ' )
307
- end
308
- buf
309
- end
310
-
311
-
312
202
  end # class TxtMatchWriter
313
203
  end # module SportDb
@@ -3,8 +3,8 @@ module SportDb
3
3
  module Module
4
4
  module Writers
5
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
6
- MINOR = 4
7
- PATCH = 2
6
+ MINOR = 5
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
@@ -26,26 +26,39 @@ end # module Writer
26
26
  ###
27
27
  # our own code
28
28
  require_relative 'writers/version'
29
- require_relative 'writers/goals'
29
+
30
+ require_relative 'writers/rounds'
31
+
32
+ require_relative 'writers/build_stats'
33
+ require_relative 'writers/build_goals'
34
+
30
35
  require_relative 'writers/txt_writer'
31
36
 
32
37
 
33
38
 
34
39
  module SportDb
35
40
  class TxtMatchWriter
41
+
42
+
43
+
44
+
45
+ ## fix - change name: to title: !!!!
46
+ ## fix: remove rounds: true|false - make it works without rounds without flag!!!!!
47
+
36
48
  def self.write( path, matches, name:, rounds: true)
37
49
 
38
50
  buf = build( matches, rounds: rounds )
39
-
40
- ## for convenience - make sure parent folders/directories exist
41
- FileUtils.mkdir_p( File.dirname( path) ) unless Dir.exist?( File.dirname( path ))
42
-
51
+
43
52
  puts "==> writing to >#{path}<..."
44
- File.open( path, 'w:utf-8' ) do |f|
45
- f.write( "= #{name}\n" )
46
- f.write( buf )
47
- end
53
+
54
+ txt = "= #{name}\n\n" + buf
55
+ write_text( path, txt )
48
56
  end # method self.write
57
+
58
+ class << self
59
+ alias_method :write_v2, :write
60
+ end
61
+
49
62
  end # class TxtMatchWriter
50
63
  end # module SportDb
51
64
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-writers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
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: 2024-12-30 00:00:00.000000000 Z
11
+ date: 2026-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sportdb-structs
@@ -73,7 +73,9 @@ files:
73
73
  - README.md
74
74
  - Rakefile
75
75
  - lib/sportdb/writers.rb
76
- - lib/sportdb/writers/goals.rb
76
+ - lib/sportdb/writers/build_goals.rb
77
+ - lib/sportdb/writers/build_stats.rb
78
+ - lib/sportdb/writers/rounds.rb
77
79
  - lib/sportdb/writers/txt_writer.rb
78
80
  - lib/sportdb/writers/version.rb
79
81
  homepage: https://github.com/sportdb/sport.db