sportdb-writers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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