sportdb-writers 0.0.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,20 @@
1
+ module Writer
2
+
3
+ LEAGUES.merge!(
4
+
5
+ ########################
6
+ # Italy
7
+ 'it.1' => { name: 'Italian Serie A',
8
+ basename: '1-seriea',
9
+ path: 'italy',
10
+ lang: 'it',
11
+ },
12
+ 'it.2' => { name: 'Italian Serie B',
13
+ basename: '2-serieb',
14
+ path: 'italy',
15
+ lang: 'it',
16
+ },
17
+
18
+ )
19
+
20
+ end # module Writer
@@ -0,0 +1,25 @@
1
+ module Writer
2
+
3
+ LEAGUES.merge!(
4
+
5
+ ##############################
6
+ # Mexico
7
+ #
8
+ # - Viertelfinale
9
+ # - Halbfinale
10
+ # - Finale
11
+
12
+ 'mx.1' => { name: 'Liga MX',
13
+ basename: '1-ligamx', ## note: gets "overwritten" by stages (see below)
14
+ path: 'mexico',
15
+ lang: 'es',
16
+ stages: [{basename: '1-apertura', names: ['Apertura']},
17
+ {basename: '1-apertura_liguilla', names: ['Apertura - Liguilla']},
18
+ {basename: '1-clausura', names: ['Clausura']},
19
+ {basename: '1-clausura_liguilla', names: ['Clausura - Liguilla']},
20
+ ],
21
+ },
22
+ )
23
+
24
+
25
+ end # module Writer
@@ -0,0 +1,21 @@
1
+ module Writer
2
+
3
+
4
+ LEAGUES.merge!(
5
+ 'ar.1' => { name: 'Argentina Primera Division',
6
+ basename: '1-primeradivision',
7
+ path: 'south-america/argentina',
8
+ lang: 'es_AR',
9
+ },
10
+
11
+ ############################
12
+ # Brazil
13
+ 'br.1' => { name: 'Brasileiro Série A', ## league name
14
+ basename: '1-seriea',
15
+ path: 'south-america/brazil', ## repo path
16
+ lang: 'pt_BR',
17
+ },
18
+ )
19
+
20
+
21
+ end # module Writer
@@ -0,0 +1,16 @@
1
+
2
+ module Writer
3
+
4
+ LEAGUES.merge!(
5
+ 'cn.1' => { name: 'Chinese Super League',
6
+ basename: '1-superleague',
7
+ path: 'world/asia/china',
8
+ },
9
+ 'jp.1' => { name: 'Japan J1 League',
10
+ basename: '1-j1league',
11
+ path: 'world/asia/japan',
12
+ },
13
+ )
14
+
15
+ end # module Writer
16
+
@@ -0,0 +1,31 @@
1
+ ## just use sportdb/catalogs ?! - why? why not?
2
+ # require 'sportdb/importers' # -- requires db support
3
+ # require 'sportdb/readers' # -- requires db support
4
+ require 'sportdb/catalogs'
5
+
6
+
7
+ ###
8
+ # our own code
9
+ require 'sportdb/writers/version'
10
+ require 'sportdb/writers/config'
11
+ require 'sportdb/writers/txt_writer'
12
+ require 'sportdb/writers/write'
13
+
14
+ ## setup empty leagues (info) hash
15
+ module Writer
16
+ LEAGUES = {}
17
+ end
18
+
19
+ require 'sportdb/leagues/leagues_at'
20
+ require 'sportdb/leagues/leagues_de'
21
+ require 'sportdb/leagues/leagues_eng'
22
+ require 'sportdb/leagues/leagues_es'
23
+ require 'sportdb/leagues/leagues_europe'
24
+ require 'sportdb/leagues/leagues_it'
25
+ require 'sportdb/leagues/leagues_mx'
26
+ require 'sportdb/leagues/leagues_south_america'
27
+ require 'sportdb/leagues/leagues_world'
28
+
29
+
30
+
31
+ puts SportDb::Module::Writers.banner # say hello
@@ -0,0 +1,18 @@
1
+ module Writer
2
+
3
+ class Configuration
4
+ def out_dir() @out_dir || './tmp'; end
5
+ def out_dir=(value) @out_dir = value; end
6
+ end # class Configuration
7
+
8
+
9
+ ## lets you use
10
+ ## Write.configure do |config|
11
+ ## config.out_dir = "??"
12
+ ## end
13
+
14
+ def self.configure() yield( config ); end
15
+
16
+ def self.config() @config ||= Configuration.new; end
17
+
18
+ end # module Writer
@@ -0,0 +1,407 @@
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