sportdb-writers 0.0.1 → 0.1.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.
@@ -1,407 +1,225 @@
1
- module SportDb
2
- class TxtMatchWriter
3
-
4
-
5
- DE_WEEKDAY = {
6
- 1 => 'Mo', ## Montag
7
- 2 => 'Di', ## Dienstag
8
- 3 => 'Mi', ## Mittwoch
9
- 4 => 'Do', ## Donnerstag
10
- 5 => 'Fr', ## Freitag
11
- 6 => 'Sa', ## Samstag
12
- 7 => 'So', ## Sonntag
13
- }
14
-
15
- ##
16
- # https://en.wikipedia.org/wiki/Date_and_time_notation_in_Spain
17
- ES_WEEKDAY = {
18
- 1 => 'Lun', ## lunes ## todo: fix!! was Lue - why?
19
- 2 => 'Mar', ## martes
20
- 3 => 'Mié', ## miércoles
21
- 4 => 'Jue', ## jueves
22
- 5 => 'Vie', ## viernes
23
- 6 => 'Sáb', ## sábado ## todo: fix!! was Sab - why?
24
- 7 => 'Dom', ## domingo
25
- }
26
-
27
-
28
- PT_WEEKDAY = {
29
- 1 => 'Seg',
30
- 2 => 'Ter',
31
- 3 => 'Qua',
32
- 4 => 'Qui',
33
- 5 => 'Sex',
34
- 6 => 'Sáb',
35
- 7 => 'Dom',
36
- }
37
-
38
- PT_MONTH = {
39
- 1 => 'Jan',
40
- 2 => 'Fev',
41
- 3 => 'Março',
42
- 4 => 'Abril',
43
- 5 => 'Maio',
44
- 6 => 'Junho',
45
- 7 => 'Julho',
46
- 8 => 'Agosto',
47
- 9 => 'Set',
48
- 10 => 'Out',
49
- 11 => 'Nov',
50
- 12 => 'Dez',
51
- }
52
-
53
- ##
54
- # https://en.wikipedia.org/wiki/Date_and_time_notation_in_Italy
55
- IT_WEEKDAY = {
56
- 1 => 'Lun', ## Lunedì
57
- 2 => 'Mar', ## Martedì
58
- 3 => 'Mer', ## Mercoledì
59
- 4 => 'Gio', ## Giovedì
60
- 5 => 'Ven', ## Venerdì
61
- 6 => 'Sab', ## Sabato
62
- 7 => 'Dom', ## Domenica
63
- }
64
-
65
- FR_WEEKDAY = {
66
- 1 => 'Lun',
67
- 2 => 'Mar ',
68
- 3 => 'Mer',
69
- 4 => 'Jeu',
70
- 5 => 'Ven',
71
- 6 => 'Sam',
72
- 7 => 'Dim',
73
- }
74
-
75
- FR_MONTH = {
76
- 1 => 'Jan',
77
- 2 => 'Fév',
78
- 3 => 'Mars',
79
- 4 => 'Avril',
80
- 5 => 'Mai',
81
- 6 => 'Juin',
82
- 7 => 'Juil',
83
- 8 => 'Août',
84
- 9 => 'Sept',
85
- 10 => 'Oct',
86
- 11 => 'Nov',
87
- 12 => 'Déc',
88
- }
89
-
90
-
91
- EN_WEEKDAY = {
92
- 1 => 'Mon',
93
- 2 => 'Tue',
94
- 3 => 'Wed',
95
- 4 => 'Thu',
96
- 5 => 'Fri',
97
- 6 => 'Sat',
98
- 7 => 'Sun',
99
- }
100
-
101
-
102
-
103
- DATES =
104
- {
105
- ## english (en) -- e.g. Mon Aug/11
106
- 'en' => ->(date) { buf = String.new('')
107
- buf << EN_WEEKDAY[date.cwday]
108
- buf << ' '
109
- buf << date.strftime( '%b/%-d' )
110
- buf
111
- },
112
- ## portuguese (pt) -- e.g. Sáb, 13/Maio or Sáb 13 Maio
113
- 'pt' => ->(date) { buf = String.new('')
114
- buf << PT_WEEKDAY[date.cwday]
115
- buf << ", #{date.day}/"
116
- buf << PT_MONTH[date.month]
117
- buf
118
- },
119
- ## german / deutsch (de) -- e.g. Mo 11.8.
120
- 'de' => ->(date) { buf = String.new('')
121
- buf << DE_WEEKDAY[date.cwday]
122
- buf << ' '
123
- buf << date.strftime( '%-d.%-m.' )
124
- buf
125
- },
126
- ## italian / italiano (it) -- e.g. Lun. 11.8.
127
- 'it' => ->(date) { buf = String.new('')
128
- buf << IT_WEEKDAY[date.cwday]
129
- buf << '. '
130
- buf << date.strftime( '%-d.%-m.' )
131
- buf
132
- },
133
- ## spanish / espanol (es) -- e.g. Lun. 11.8.
134
- 'es' => ->(date) { buf = String.new('')
135
- buf << ES_WEEKDAY[date.cwday]
136
- buf << '. '
137
- buf << date.strftime( '%-d.%-m.' )
138
- buf
139
- },
140
- ## french / francais (fr)
141
- 'fr' => ->( date ) { buf = String.new('')
142
- buf << FR_WEEKDAY[date.cwday]
143
- buf << " #{date.day}. "
144
- buf << FR_MONTH[date.month]
145
- buf
146
- },
147
- }
148
-
149
-
150
- SCORES =
151
- {
152
- 'en' => ->( match ) { match.score.to_s( lang: 'en' ) },
153
- 'de' => ->( match ) { match.score.to_s( lang: 'de' ) },
154
- }
155
-
156
-
157
- LANGS =
158
- {
159
- 'en' => { round: 'Matchday', date: DATES['en'], score: SCORES['en'] },
160
- 'en_AU' => { round: 'Round', date: DATES['en'], score: SCORES['en'] },
161
- 'pt' => { round: 'Jornada', date: DATES['pt'], score: SCORES['en'] },
162
- 'pt_BR' => { round: 'Rodada', date: DATES['pt'], score: SCORES['en'] },
163
- 'it' => { round: ->(round) { "%s^ Giornata" % round }, date: DATES['it'], score: SCORES['en'] },
164
- 'fr' => { round: 'Journée', date: DATES['fr'], score: SCORES['en'] },
165
- 'es' => { round: 'Jornada',
166
- date: DATES['es'],
167
- score: SCORES['en'],
168
- ## add simple en-to-es translation for now
169
- translations: {
170
- 'Quarterfinals' => 'Cuartos de Final',
171
- 'Semifinals' => 'Semifinales',
172
- 'Finals' => 'Final',
173
- },
174
- },
175
- 'de' => { round: 'Spieltag', date: DATES['de'], score: SCORES['de'] },
176
- 'de_AT' => { round: ->(round) { "%s. Runde" % round }, date: DATES['de'], score: SCORES['de'] },
177
- }
178
-
179
-
180
-
181
-
182
-
183
- ## add 1:1 (more) convenience aliases
184
- LANGS[ 'de_DE' ] = LANGS[ 'de']
185
- LANGS[ 'de_CH' ] = LANGS[ 'de']
186
- LANGS[ 'pt_PT' ] = LANGS[ 'pt']
187
- LANGS[ 'es_AR' ] = LANGS[ 'es']
188
-
189
-
190
-
191
-
192
- ## note: build returns buf - an (in-memory) string buf(fer)
193
- def self.build( matches, lang: 'en', rounds: true )
194
- ## note: make sure rounds is a bool, that is, true or false (do NOT pass in strings etc.)
195
- raise ArgumentError, "rounds flag - bool expected; got: #{rounds.inspect}" unless rounds.is_a?( TrueClass ) || rounds.is_a?( FalseClass )
196
-
197
-
198
- defaults = LANGS[ lang ] || LANGS[ 'en' ] ## note: fallback for now to english if no defaults defined for lang
199
-
200
- round = defaults[ :round ]
201
- format_date = defaults[ :date ]
202
- format_score = defaults[ :score ]
203
- round_translations = defaults[ :translations ]
204
-
205
-
206
- buf = String.new('')
207
-
208
- last_round = nil
209
- last_date = nil
210
- last_time = nil
211
-
212
-
213
- matches.each do |match|
214
-
215
- ## note: make rounds optional (set rounds flag to false to turn off)
216
- if rounds
217
- if match.round != last_round
218
- buf << "\n\n"
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
+ if match.round.nil?
50
+ ## warn
51
+ puts "!! ERROR - match with round nil?"
52
+ pp match
53
+ exit 1
54
+ end
55
+
56
+ buf << (round_translations[match.round] || match.round)
57
+ end
58
+ buf << "\n"
59
+ end
60
+ end
61
+
62
+
63
+ date = if match.date.is_a?( String )
64
+ Date.strptime( match.date, '%Y-%m-%d' )
65
+ else ## assume it's already a date (object)
66
+ match.date
67
+ end
68
+
69
+ time = if match.time.is_a?( String )
70
+ Time.strptime( match.time, '%H:%M')
71
+ else ## assume it's already a time (object) or nil
72
+ match.time
73
+ end
74
+
75
+
76
+ date_yyyymmdd = date.strftime( '%Y-%m-%d' )
77
+
78
+ ## note: time is OPTIONAL for now
79
+ ## note: use 17.00 and NOT 17:00 for now
80
+ time_hhmm = time ? time.strftime( '%H.%M' ) : nil
81
+
82
+
83
+ if rounds
84
+ if match.round != last_round || date_yyyymmdd != last_date
85
+ buf << "[#{format_date.call( date )}]\n"
86
+ last_time = nil ## note: reset time for new date
87
+ end
88
+ else
89
+ if date_yyyymmdd != last_date
90
+ buf << "\n" ## note: add an extra leading blank line (if no round headings printed)
91
+ buf << "[#{format_date.call( date )}]\n"
92
+ last_time = nil
93
+ end
94
+ end
95
+
96
+
97
+ ## allow strings and structs for team names
98
+ team1 = match.team1.is_a?( String ) ? match.team1 : match.team1.name
99
+ team2 = match.team2.is_a?( String ) ? match.team2 : match.team2.name
100
+
101
+
102
+ line = String.new
103
+ line << ' '
104
+
105
+ if time
106
+ if last_time.nil? || last_time != time_hhmm
107
+ line << "%5s" % time_hhmm
108
+ else
109
+ line << ' '
110
+ end
111
+ line << ' '
112
+ else
113
+ ## do nothing for now
114
+ end
115
+
116
+
117
+ line << "%-23s" % team1 ## note: use %-s for left-align
118
+
119
+ line << " #{format_score.call( match )} " ## note: separate by at least two spaces for now
120
+
121
+ line << "%-23s" % team2
122
+
123
+
124
+ if match.status
125
+ line << ' '
126
+ case match.status
127
+ when Status::CANCELLED
128
+ line << '[cancelled]'
129
+ when Status::AWARDED
130
+ line << '[awarded]'
131
+ when Status::ABANDONED
132
+ line << '[abandoned]'
133
+ when Status::REPLAY
134
+ line << '[replay]'
135
+ when Status::POSTPONED
136
+ ## note: add NOTHING for postponed for now
137
+ else
138
+ puts "!! WARN - unknown match status >#{match.status}<:"
139
+ pp match
140
+ line << "[#{match.status.downcase}]" ## print "literal" downcased for now
141
+ end
142
+ end
143
+
144
+ ## add match line
145
+ buf << line.rstrip ## remove possible right trailing spaces before adding
146
+ buf << "\n"
147
+
148
+ if match.goals
149
+ buf << ' ' # 4 space indent
150
+ buf << ' ' if time # 7 (5+2) space indent (for hour e.g. 17.30)
151
+ buf << "[#{build_goals(match.goals)}]"
152
+ buf << "\n"
153
+ end
154
+
155
+
156
+ last_round = match.round
157
+ last_date = date_yyyymmdd
158
+ last_time = time_hhmm
159
+ end
160
+ buf
161
+ end
162
+
163
+
164
+ def self.write( path, matches, name:, rounds: true)
165
+
166
+ buf = build( matches, rounds: rounds )
167
+
168
+ ## for convenience - make sure parent folders/directories exist
169
+ FileUtils.mkdir_p( File.dirname( path) ) unless Dir.exist?( File.dirname( path ))
170
+
171
+ puts "==> writing to >#{path}<..."
172
+ File.open( path, 'w:utf-8' ) do |f|
173
+ f.write( "= #{name}\n" )
174
+ f.write( buf )
175
+ end
176
+ end # method self.write
177
+
178
+
179
+ def self.build_goals( goals )
180
+ ## todo/fix: for now assumes always minutes (without offset) - add offset support
181
+
182
+ ## note: "fold" multiple goals by players
183
+ team1_goals = {}
184
+ team2_goals = {}
185
+ goals.each do |goal|
186
+ team_goals = goal.team == 1 ? team1_goals : team2_goals
187
+ player_goals = team_goals[ goal.player ] ||= []
188
+ player_goals << goal
189
+ end
190
+
191
+ buf = String.new
192
+ if team1_goals.size > 0
193
+ buf << build_goals_for_team( team1_goals )
194
+ end
195
+
196
+ ## note: only add a separator (;) if BOTH teams have goal scores
197
+ if team1_goals.size > 0 && team2_goals.size > 0
198
+ buf << '; '
199
+ end
200
+
201
+ if team2_goals.size > 0
202
+ buf << build_goals_for_team( team2_goals )
203
+ end
204
+ buf
205
+ end
206
+
207
+
208
+ def self.build_goals_for_team( team_goals )
209
+ buf = String.new
210
+ team_goals.each_with_index do |(player_name, goals),i|
211
+ buf << ' ' if i > 0
212
+ buf << "#{player_name} "
213
+ buf << goals.map do |goal|
214
+ str = "#{goal.minute}'"
215
+ str << " (o.g.)" if goal.owngoal?
216
+ str << " (pen.)" if goal.penalty?
217
+ str
218
+ end.join( ', ' )
219
+ end
220
+ buf
221
+ end
222
+
223
+
224
+ end # class TxtMatchWriter
225
+ end # module SportDb