sportdb-writers 0.0.1 → 0.1.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 +5 -5
- data/CHANGELOG.md +6 -4
- data/Manifest.txt +2 -4
- data/README.md +26 -28
- data/Rakefile +32 -30
- data/lib/sportdb/leagues/leagues_at.rb +35 -39
- data/lib/sportdb/leagues/leagues_de.rb +21 -29
- data/lib/sportdb/leagues/leagues_eng.rb +58 -70
- data/lib/sportdb/leagues/leagues_es.rb +15 -19
- data/lib/sportdb/leagues/leagues_europe.rb +178 -213
- data/lib/sportdb/leagues/leagues_it.rb +16 -20
- data/lib/sportdb/leagues/leagues_mx.rb +23 -25
- data/lib/sportdb/leagues/leagues_south_america.rb +17 -21
- data/lib/sportdb/leagues/leagues_world.rb +14 -16
- data/lib/sportdb/writers/github.rb +195 -0
- data/lib/sportdb/writers/goals.rb +57 -0
- data/lib/sportdb/writers/txt_writer.rb +218 -407
- data/lib/sportdb/writers/version.rb +24 -24
- data/lib/sportdb/writers/write.rb +269 -367
- data/lib/sportdb/writers.rb +97 -31
- metadata +42 -17
- data/lib/sportdb/writers/config.rb +0 -18
- data/test/helper.rb +0 -19
- data/test/test_txt_writer.rb +0 -124
- data/test/test_version.rb +0 -16
@@ -1,407 +1,218 @@
|
|
1
|
-
module SportDb
|
2
|
-
class TxtMatchWriter
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
}
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
##
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
'
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
}
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
if match.round.is_a?( Integer ) ||
|
220
|
-
match.round =~ /^[0-9]+$/ ## all numbers/digits
|
221
|
-
if round.is_a?( Proc )
|
222
|
-
buf << round.call( match.round )
|
223
|
-
else
|
224
|
-
## default "class format
|
225
|
-
## e.g. Runde 1, Spieltag 1, Matchday 1, Week 1
|
226
|
-
buf << "#{round} #{match.round}"
|
227
|
-
end
|
228
|
-
else ## use as is from match
|
229
|
-
## note: for now assume english names
|
230
|
-
if round_translations
|
231
|
-
buf << "#{round_translations[match.round] || match.round}"
|
232
|
-
else
|
233
|
-
buf << "#{match.round}"
|
234
|
-
end
|
235
|
-
end
|
236
|
-
buf << "\n"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
|
241
|
-
date = if match.date.is_a?( String )
|
242
|
-
Date.strptime( match.date, '%Y-%m-%d' )
|
243
|
-
else ## assume it's already a date (object)
|
244
|
-
match.date
|
245
|
-
end
|
246
|
-
|
247
|
-
time = if match.time.is_a?( String )
|
248
|
-
Time.strptime( match.time, '%H:%M')
|
249
|
-
else ## assume it's already a time (object) or nil
|
250
|
-
match.time
|
251
|
-
end
|
252
|
-
|
253
|
-
|
254
|
-
date_yyyymmdd = date.strftime( '%Y-%m-%d' )
|
255
|
-
|
256
|
-
## note: time is OPTIONAL for now
|
257
|
-
## note: use 17.00 and NOT 17:00 for now
|
258
|
-
time_hhmm = time ? time.strftime( '%H.%M' ) : nil
|
259
|
-
|
260
|
-
|
261
|
-
if rounds
|
262
|
-
if match.round != last_round || date_yyyymmdd != last_date
|
263
|
-
buf << "[#{format_date.call( date )}]\n"
|
264
|
-
last_time = nil ## note: reset time for new date
|
265
|
-
end
|
266
|
-
else
|
267
|
-
if date_yyyymmdd != last_date
|
268
|
-
buf << "\n" ## note: add an extra leading blank line (if no round headings printed)
|
269
|
-
buf << "[#{format_date.call( date )}]\n"
|
270
|
-
last_time = nil
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
|
275
|
-
## allow strings and structs for team names
|
276
|
-
team1 = match.team1.is_a?( String ) ? match.team1 : match.team1.name
|
277
|
-
team2 = match.team2.is_a?( String ) ? match.team2 : match.team2.name
|
278
|
-
|
279
|
-
|
280
|
-
line = String.new('')
|
281
|
-
line << ' '
|
282
|
-
|
283
|
-
if time
|
284
|
-
if last_time.nil? || last_time != time_hhmm
|
285
|
-
line << "%5s" % time_hhmm
|
286
|
-
else
|
287
|
-
line << ' '
|
288
|
-
end
|
289
|
-
line << ' '
|
290
|
-
else
|
291
|
-
## do nothing for now
|
292
|
-
end
|
293
|
-
|
294
|
-
|
295
|
-
line << "%-23s" % team1 ## note: use %-s for left-align
|
296
|
-
|
297
|
-
line << " #{format_score.call( match )} " ## note: separate by at least two spaces for now
|
298
|
-
|
299
|
-
line << "%-23s" % team2
|
300
|
-
|
301
|
-
|
302
|
-
if match.status
|
303
|
-
line << ' '
|
304
|
-
case match.status
|
305
|
-
when Status::CANCELLED
|
306
|
-
line << '[cancelled]'
|
307
|
-
when Status::AWARDED
|
308
|
-
line << '[awarded]'
|
309
|
-
when Status::ABANDONED
|
310
|
-
line << '[abandoned]'
|
311
|
-
when Status::REPLAY
|
312
|
-
line << '[replay]'
|
313
|
-
when Status::POSTPONED
|
314
|
-
## note: add NOTHING for postponed for now
|
315
|
-
else
|
316
|
-
puts "!! WARN - unknown match status >#{match.status}<:"
|
317
|
-
pp match
|
318
|
-
line << "[#{match.status.downcase}]" ## print "literal" downcased for now
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
## add match line
|
323
|
-
buf << line.rstrip ## remove possible right trailing spaces before adding
|
324
|
-
buf << "\n"
|
325
|
-
|
326
|
-
if match.goals
|
327
|
-
buf << ' ' # 4 space indent
|
328
|
-
buf << ' ' if time # 7 (5+2) space indent (for hour e.g. 17.30)
|
329
|
-
buf << "[#{build_goals(match.goals, lang: lang )}]"
|
330
|
-
buf << "\n"
|
331
|
-
end
|
332
|
-
|
333
|
-
|
334
|
-
last_round = match.round
|
335
|
-
last_date = date_yyyymmdd
|
336
|
-
last_time = time_hhmm
|
337
|
-
end
|
338
|
-
buf
|
339
|
-
end
|
340
|
-
|
341
|
-
|
342
|
-
def self.write( path, matches, name:, lang: 'en', rounds: true)
|
343
|
-
|
344
|
-
buf = build( matches, lang: lang, rounds: rounds )
|
345
|
-
|
346
|
-
## for convenience - make sure parent folders/directories exist
|
347
|
-
FileUtils.mkdir_p( File.dirname( path) ) unless Dir.exists?( File.dirname( path ))
|
348
|
-
|
349
|
-
File.open( path, 'w:utf-8' ) do |f|
|
350
|
-
f.write( "= #{name}\n" )
|
351
|
-
f.write( buf )
|
352
|
-
end
|
353
|
-
end # method self.write
|
354
|
-
|
355
|
-
|
356
|
-
def self.build_goals( goals, lang: )
|
357
|
-
## todo/fix: for now assumes always minutes (without offset) - add offset support
|
358
|
-
|
359
|
-
## note: "fold" multiple goals by players
|
360
|
-
team1_goals = {}
|
361
|
-
team2_goals = {}
|
362
|
-
goals.each do |goal|
|
363
|
-
team_goals = goal.team == 1 ? team1_goals : team2_goals
|
364
|
-
player_goals = team_goals[ goal.player ] ||= []
|
365
|
-
player_goals << goal
|
366
|
-
end
|
367
|
-
|
368
|
-
buf = String.new('')
|
369
|
-
if team1_goals.size > 0
|
370
|
-
buf << build_goals_for_team( team1_goals, lang: lang )
|
371
|
-
end
|
372
|
-
|
373
|
-
## note: only add a separator (;) if BOTH teams have goal scores
|
374
|
-
if team1_goals.size > 0 && team2_goals.size > 0
|
375
|
-
buf << '; '
|
376
|
-
end
|
377
|
-
|
378
|
-
if team2_goals.size > 0
|
379
|
-
buf << build_goals_for_team( team2_goals, lang: lang )
|
380
|
-
end
|
381
|
-
buf
|
382
|
-
end
|
383
|
-
|
384
|
-
|
385
|
-
def self.build_goals_for_team( team_goals, lang: )
|
386
|
-
buf = String.new('')
|
387
|
-
team_goals.each_with_index do |(player_name, goals),i|
|
388
|
-
buf << ' ' if i > 0
|
389
|
-
buf << "#{player_name} "
|
390
|
-
buf << goals.map do |goal|
|
391
|
-
str = "#{goal.minute}'"
|
392
|
-
if ['de', 'de_AT', 'de_DE', 'de_CH'].include?( lang )
|
393
|
-
str << " (Eigentor)" if goal.owngoal?
|
394
|
-
str << " (Elf.)" if goal.penalty?
|
395
|
-
else ## fallback to english (by default)
|
396
|
-
str << " (o.g.)" if goal.owngoal?
|
397
|
-
str << " (pen.)" if goal.penalty?
|
398
|
-
end
|
399
|
-
str
|
400
|
-
end.join( ', ' )
|
401
|
-
end
|
402
|
-
buf
|
403
|
-
end
|
404
|
-
|
405
|
-
|
406
|
-
end # class TxtMatchWriter
|
407
|
-
end # module SportDb
|
1
|
+
module SportDb
|
2
|
+
class TxtMatchWriter
|
3
|
+
|
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
|
+
|
17
|
+
## note: build returns buf - an (in-memory) string buf(fer)
|
18
|
+
def self.build( matches, rounds: true )
|
19
|
+
## note: make sure rounds is a bool, that is, true or false (do NOT pass in strings etc.)
|
20
|
+
raise ArgumentError, "rounds flag - bool expected; got: #{rounds.inspect}" unless rounds.is_a?( TrueClass ) || rounds.is_a?( FalseClass )
|
21
|
+
|
22
|
+
## note: for now always english
|
23
|
+
round = 'Matchday'
|
24
|
+
format_date = ->(date) { date.strftime( '%a %b/%-d' ) }
|
25
|
+
format_score = ->(match) { match.score.to_s( lang: 'en' ) }
|
26
|
+
round_translations = ROUND_TRANSLATIONS
|
27
|
+
|
28
|
+
|
29
|
+
buf = String.new
|
30
|
+
|
31
|
+
last_round = nil
|
32
|
+
last_date = nil
|
33
|
+
last_time = nil
|
34
|
+
|
35
|
+
|
36
|
+
matches.each do |match|
|
37
|
+
|
38
|
+
## note: make rounds optional (set rounds flag to false to turn off)
|
39
|
+
if rounds
|
40
|
+
if match.round != last_round
|
41
|
+
buf << "\n\n"
|
42
|
+
if match.round.is_a?( Integer ) ||
|
43
|
+
match.round =~ /^[0-9]+$/ ## all numbers/digits
|
44
|
+
## default "class format
|
45
|
+
## e.g. Runde 1, Spieltag 1, Matchday 1, Week 1
|
46
|
+
buf << "#{round} #{match.round}"
|
47
|
+
else ## use as is from match
|
48
|
+
## note: for now assume english names
|
49
|
+
buf << round_translations[match.round] || match.round
|
50
|
+
end
|
51
|
+
buf << "\n"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
date = if match.date.is_a?( String )
|
57
|
+
Date.strptime( match.date, '%Y-%m-%d' )
|
58
|
+
else ## assume it's already a date (object)
|
59
|
+
match.date
|
60
|
+
end
|
61
|
+
|
62
|
+
time = if match.time.is_a?( String )
|
63
|
+
Time.strptime( match.time, '%H:%M')
|
64
|
+
else ## assume it's already a time (object) or nil
|
65
|
+
match.time
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
date_yyyymmdd = date.strftime( '%Y-%m-%d' )
|
70
|
+
|
71
|
+
## note: time is OPTIONAL for now
|
72
|
+
## note: use 17.00 and NOT 17:00 for now
|
73
|
+
time_hhmm = time ? time.strftime( '%H.%M' ) : nil
|
74
|
+
|
75
|
+
|
76
|
+
if rounds
|
77
|
+
if match.round != last_round || date_yyyymmdd != last_date
|
78
|
+
buf << "[#{format_date.call( date )}]\n"
|
79
|
+
last_time = nil ## note: reset time for new date
|
80
|
+
end
|
81
|
+
else
|
82
|
+
if date_yyyymmdd != last_date
|
83
|
+
buf << "\n" ## note: add an extra leading blank line (if no round headings printed)
|
84
|
+
buf << "[#{format_date.call( date )}]\n"
|
85
|
+
last_time = nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
## allow strings and structs for team names
|
91
|
+
team1 = match.team1.is_a?( String ) ? match.team1 : match.team1.name
|
92
|
+
team2 = match.team2.is_a?( String ) ? match.team2 : match.team2.name
|
93
|
+
|
94
|
+
|
95
|
+
line = String.new
|
96
|
+
line << ' '
|
97
|
+
|
98
|
+
if time
|
99
|
+
if last_time.nil? || last_time != time_hhmm
|
100
|
+
line << "%5s" % time_hhmm
|
101
|
+
else
|
102
|
+
line << ' '
|
103
|
+
end
|
104
|
+
line << ' '
|
105
|
+
else
|
106
|
+
## do nothing for now
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
line << "%-23s" % team1 ## note: use %-s for left-align
|
111
|
+
|
112
|
+
line << " #{format_score.call( match )} " ## note: separate by at least two spaces for now
|
113
|
+
|
114
|
+
line << "%-23s" % team2
|
115
|
+
|
116
|
+
|
117
|
+
if match.status
|
118
|
+
line << ' '
|
119
|
+
case match.status
|
120
|
+
when Status::CANCELLED
|
121
|
+
line << '[cancelled]'
|
122
|
+
when Status::AWARDED
|
123
|
+
line << '[awarded]'
|
124
|
+
when Status::ABANDONED
|
125
|
+
line << '[abandoned]'
|
126
|
+
when Status::REPLAY
|
127
|
+
line << '[replay]'
|
128
|
+
when Status::POSTPONED
|
129
|
+
## note: add NOTHING for postponed for now
|
130
|
+
else
|
131
|
+
puts "!! WARN - unknown match status >#{match.status}<:"
|
132
|
+
pp match
|
133
|
+
line << "[#{match.status.downcase}]" ## print "literal" downcased for now
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
## add match line
|
138
|
+
buf << line.rstrip ## remove possible right trailing spaces before adding
|
139
|
+
buf << "\n"
|
140
|
+
|
141
|
+
if match.goals
|
142
|
+
buf << ' ' # 4 space indent
|
143
|
+
buf << ' ' if time # 7 (5+2) space indent (for hour e.g. 17.30)
|
144
|
+
buf << "[#{build_goals(match.goals)}]"
|
145
|
+
buf << "\n"
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
last_round = match.round
|
150
|
+
last_date = date_yyyymmdd
|
151
|
+
last_time = time_hhmm
|
152
|
+
end
|
153
|
+
buf
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def self.write( path, matches, name:, rounds: true)
|
158
|
+
|
159
|
+
buf = build( matches, rounds: rounds )
|
160
|
+
|
161
|
+
## for convenience - make sure parent folders/directories exist
|
162
|
+
FileUtils.mkdir_p( File.dirname( path) ) unless Dir.exist?( File.dirname( path ))
|
163
|
+
|
164
|
+
puts "==> writing to >#{path}<..."
|
165
|
+
File.open( path, 'w:utf-8' ) do |f|
|
166
|
+
f.write( "= #{name}\n" )
|
167
|
+
f.write( buf )
|
168
|
+
end
|
169
|
+
end # method self.write
|
170
|
+
|
171
|
+
|
172
|
+
def self.build_goals( goals )
|
173
|
+
## todo/fix: for now assumes always minutes (without offset) - add offset support
|
174
|
+
|
175
|
+
## note: "fold" multiple goals by players
|
176
|
+
team1_goals = {}
|
177
|
+
team2_goals = {}
|
178
|
+
goals.each do |goal|
|
179
|
+
team_goals = goal.team == 1 ? team1_goals : team2_goals
|
180
|
+
player_goals = team_goals[ goal.player ] ||= []
|
181
|
+
player_goals << goal
|
182
|
+
end
|
183
|
+
|
184
|
+
buf = String.new
|
185
|
+
if team1_goals.size > 0
|
186
|
+
buf << build_goals_for_team( team1_goals )
|
187
|
+
end
|
188
|
+
|
189
|
+
## note: only add a separator (;) if BOTH teams have goal scores
|
190
|
+
if team1_goals.size > 0 && team2_goals.size > 0
|
191
|
+
buf << '; '
|
192
|
+
end
|
193
|
+
|
194
|
+
if team2_goals.size > 0
|
195
|
+
buf << build_goals_for_team( team2_goals )
|
196
|
+
end
|
197
|
+
buf
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
def self.build_goals_for_team( team_goals )
|
202
|
+
buf = String.new
|
203
|
+
team_goals.each_with_index do |(player_name, goals),i|
|
204
|
+
buf << ' ' if i > 0
|
205
|
+
buf << "#{player_name} "
|
206
|
+
buf << goals.map do |goal|
|
207
|
+
str = "#{goal.minute}'"
|
208
|
+
str << " (o.g.)" if goal.owngoal?
|
209
|
+
str << " (pen.)" if goal.penalty?
|
210
|
+
str
|
211
|
+
end.join( ', ' )
|
212
|
+
end
|
213
|
+
buf
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
end # class TxtMatchWriter
|
218
|
+
end # module SportDb
|