Class: SportDb::TxtMatchWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/sportdb/writers.rb,
lib/sportdb/writers/txt_writer.rb

Constant Summary collapse

ROUND_TRANSLATIONS =

translate from lang x (german, etc) to english

{
  # de/german
  '1. Runde'      => 'Round 1',
  '2. Runde'      => 'Round 2',
  'Achtelfinale'  => 'Round of 16',
  'Viertelfinale' => 'Quarterfinals',
  'Halbfinale'    => 'Semifinals',
  'Finale'        => 'Final',
}

Class Method Summary collapse

Class Method Details

._build_batch(matches, rounds: true) ⇒ Object

Raises:

  • (ArgumentError)


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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/sportdb/writers/txt_writer.rb', line 126

def self._build_batch( matches, rounds: true )
  ## note: make sure rounds is a bool, that is, true or false  (do NOT pass in strings etc.)
  raise ArgumentError, "rounds flag - bool expected; got: #{rounds.inspect}"    unless rounds.is_a?( TrueClass ) || rounds.is_a?( FalseClass )

  buf = String.new

  last_round = nil
  last_date  = nil
  last_time  = nil


  matches.each_with_index do |match,i|

     ## note: make rounds optional (set rounds flag to false to turn off)
     if rounds
       if match.round != last_round
         buf << (i == 0 ? "\n" : "\n\n")    ## start with single empty line
         if match.round.is_a?( Integer ) ||
            match.round =~ /^[0-9]+$/   ## all numbers/digits
             ## default "class format
             ##   e.g. Runde 1, Spieltag 1, Matchday 1, Week 1
             buf << "Matchday #{match.round}"
         else ## use as is from match
           ## note: for now assume english names
            if match.round.nil?
              ## warn
              puts "!! ERROR - match with round nil?"
              pp match
              exit 1
            end

           buf << (ROUND_TRANSLATIONS[match.round] || match.round)
         end
         ## note - reset last_date & last_time on every new round header
         last_date = nil
         last_time = nil

         buf << "\n"
       end
     end


     date = if match.date.is_a?( String )
               Date.strptime( match.date, '%Y-%m-%d' )
            else  ## assume it's already a date (object) or nil!!!!
               match.date
            end

     time = if match.time.is_a?( String )
              Time.strptime( match.time, '%H:%M')
            else ## assume it's already a time (object) or nil
              match.time
            end


     ## note - date might be NIL!!!!!
     date_yyyymmdd = date ? date.strftime( '%Y-%m-%d' ) : nil

     ## note: time is OPTIONAL for now
     ## note: use 17.00 and NOT 17:00 for now
     time_hhmm     = time ? time.strftime( '%H.%M' ) : nil


     if date_yyyymmdd
         if date_yyyymmdd != last_date
            ## note: add an extra leading blank line (if no round headings printed)
            buf << "\n"   unless rounds
            buf << "[#{date.strftime( '%a %b/%-d' )}]\n"
            last_time = nil
         end
     end


     ## allow strings and structs for team names
     team1 = match.team1.is_a?( String ) ? match.team1 : match.team1.name
     team2 = match.team2.is_a?( String ) ? match.team2 : match.team2.name


     line = String.new
     line << '  '

     if time
        if last_time != time_hhmm
          line << "%5s" % time_hhmm
        else
          line << '     '
        end
        line << '  '
     else
       line << '       '
     end

     line << "%-23s" % team1    ## note: use %-s for left-align

     ## note: separate by at least two spaces for now
     line << "  #{match.score.to_s( lang: 'en' )}  "  

     line << "%-23s" % team2


     if match.status
      line << '  '
      case match.status
      when Status::CANCELLED
        line << '[cancelled]'
      when Status::AWARDED
        line << '[awarded]'
      when Status::ABANDONED
        line << '[abandoned]'
      when Status::REPLAY
        line << '[replay]'
      when Status::POSTPONED
        line << '[postponed]'
        ## was -- note: add NOTHING for postponed for now
      else
        puts "!! WARN - unknown match status >#{match.status}<:"
        pp match
        line << "[#{match.status.downcase}]"  ## print "literal" downcased for now
      end
     end

     ## add match line
     buf << line.rstrip   ## remove possible right trailing spaces before adding
     buf << "\n"

     if match.goals
       buf << '    '               # 4 space indent
       buf << '       '  if time   # 7 (5+2) space indent (for hour e.g. 17.30)
       buf << "[#{build_goals(match.goals)}]"
       buf << "\n"
     end


     last_round = match.round
     last_date  = date_yyyymmdd
     last_time  = time_hhmm
  end
  buf
end

.build(matches, rounds: true) ⇒ Object

note: build returns buf - an (in-memory) string buf(fer)

Raises:

  • (ArgumentError)


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
# File 'lib/sportdb/writers/txt_writer.rb', line 18

def self.build( matches, rounds: true )
  ## note: make sure rounds is a bool, that is, true or false  (do NOT pass in strings etc.)
  raise ArgumentError, "rounds flag - bool expected; got: #{rounds.inspect}"    unless rounds.is_a?( TrueClass ) || rounds.is_a?( FalseClass )


  ### check for stages & stats
  stats = {  'stage' => Hash.new(0),
             'date' =>  { 'start_date' => nil,
                          'end_date'   => nil, },
             'teams' => Hash.new(0),
              }

## add matches played stats too??

  matches.each do |match|
     stage = match.stage
     stage = 'Regular Season'   if stage.nil? || stage.empty?
     stats['stage'][ stage ] += 1

     if match.date

      ## todo/fix - norm date (parse as Date)
      ##   check format etc.
      date = if match.date.is_a?( String )
                Date.strptime( match.date, '%Y-%m-%d' )
              else  ## assume it's already a date (object)
                match.date
              end
       stats['date']['start_date'] ||= date
       stats['date']['end_date']   ||= date

       stats['date']['start_date'] = date  if date < stats['date']['start_date']
       stats['date']['end_date']   = date  if date > stats['date']['end_date']
      end

     [match.team1, match.team2].each do |team|
        stats['teams'][ team ] += 1    if team && !['N.N.'].include?( team )
     end
  end

  use_stages =  if stats['stage'].size >= 2  ||
                   (stats['stage'].size == 1  &&
                    stats['stage'].keys[0] != 'Regular Season')
                  true
                else
                  false
                end


   ### add comment header
    buf = String.new
    # e.g. 13 April – 25 September 2024
    #  or  16 August 2024 – 25 May 2025
    ## note - date is optional!!
    if stats['date']['start_date']
      buf << "# Date       "
      start_date = stats['date']['start_date']
      end_date   = stats['date']['end_date']
      if start_date.year != end_date.year
        buf << "#{start_date.strftime('%a %b/%-d %Y')} - #{end_date.strftime('%a %b/%-d %Y')}"
      else
        buf << "#{start_date.strftime('%a %b/%-d')} - #{end_date.strftime('%a %b/%-d %Y')}"
      end
      buf << " (#{end_date.jd-start_date.jd}d)"   ## add days
      buf << "\n"
    end

    buf << "# Teams      #{stats['teams'].size}\n"
    buf << "# Matches    #{matches.size}\n"

    if use_stages
      buf << "# Stages     "
      stages = stats['stage'].map { |name,count| "#{name} (#{count})" }.join( '  ' )
      buf << stages
      buf << "\n"
    end
    buf << "\n\n"


  if use_stages
    ## split matches by stage
    matches_by_stage = {}
    matches.each do |match|
      stage = match.stage || ''
      matches_by_stage[stage] ||= []
      matches_by_stage[stage] << match
    end

    ## todo/fix
    ## note - empty stage must go first!!!!
    matches_by_stage.each_with_index do |(name, matches),i|
      buf << "\n"  if i != 0   # add extra new line (if not first stage)
      if name.empty?
        buf << "# Regular Season\n"   ## empty stage
      else
        buf << "== #{name}\n"
      end
      buf +=  _build_batch( matches, rounds: rounds )
      buf << "\n"    if i+1 != matches_by_stage.size
    end
    buf
  else
    buf += _build_batch( matches, rounds: rounds )
    buf
  end
end

.build_goals(goals) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/sportdb/writers/txt_writer.rb', line 267

def self.build_goals( goals )
  ## todo/fix: for now assumes always minutes (without offset) - add offset support

  ## note: "fold" multiple goals by players
  team1_goals = {}
  team2_goals = {}
  goals.each do |goal|
    team_goals = goal.team == 1 ? team1_goals : team2_goals
    player_goals = team_goals[ goal.player ] ||= []
    player_goals << goal
  end

  buf = String.new
  if team1_goals.size > 0
    buf << build_goals_for_team( team1_goals )
  end

  ## note: only add a separator (;) if BOTH teams have goal scores
  if team1_goals.size > 0 && team2_goals.size > 0
    buf << '; '
  end

  if team2_goals.size > 0
    buf << build_goals_for_team( team2_goals )
  end
  buf
end

.build_goals_for_team(team_goals) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/sportdb/writers/txt_writer.rb', line 296

def self.build_goals_for_team( team_goals )
  buf = String.new
  team_goals.each_with_index do |(player_name, goals),i|
    buf << ' ' if i > 0
    buf << "#{player_name} "
    buf << goals.map do |goal|
                        str = "#{goal.minute}'"
                        str << " (o.g.)"      if goal.owngoal?
                        str << " (pen.)"      if goal.penalty?
                        str
                     end.join( ', ' )
  end
  buf
end

.write(path, matches, name:, rounds: true) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/sportdb/writers.rb', line 36

def self.write( path, matches, name:, rounds: true)

  buf = build( matches, rounds: rounds )

  ## for convenience - make sure parent folders/directories exist
  FileUtils.mkdir_p( File.dirname( path) )  unless Dir.exist?( File.dirname( path ))

  puts "==> writing to >#{path}<..."
  File.open( path, 'w:utf-8' ) do |f|
    f.write( "= #{name}\n" )
    f.write( buf )
  end
end