Module: Footty

Defined in:
lib/footty/main.rb,
lib/footty/pp_week.rb,
lib/footty/version.rb,
lib/footty/pp_matches.rb,
lib/footty/dataset/dataset.rb,
lib/footty/dataset/openfootball.rb

Defined Under Namespace

Classes: Dataset, OpenfootballDataset

Constant Summary collapse

VERSION =
'2026.5.25'

Class Method Summary collapse

Class Method Details

._pp_goals(recs) ⇒ Object



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
# File 'lib/footty/pp_matches.rb', line 102

def self._pp_goals( recs )
   players = {}

   ## "fold" multiple goals of player
   recs.each do |rec|

      name = rec['name']

      if  rec['minute'].nil? || rec['minute'].empty?
        puts "!! WARN - (goals) minute empty:"
        pp rec
        ## use '??'
        rec['minute'] = '??'
        ## raise ArgumentError, "minute empty"
      end

      goal = String.new
      goal << "#{rec['minute']}'"
      goal << '(og)'   if rec['owngoal'] == true
      goal << '(p)'    if rec['penalty'] == true

      player_rec = players[ name ] ||= { name: name, goals: [] }
      player_rec[:goals] << goal
   end


   buf =  players.map do |_,player|
                    "#{player[:name]} #{player[:goals].join(',')}"
                end.join( ' ' )
   buf
end


5
6
7
# File 'lib/footty/version.rb', line 5

def self.banner
  "footty/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
end

.fmt_week(week_start, week_end) ⇒ Object



15
16
17
18
19
20
21
# File 'lib/footty/pp_week.rb', line 15

def self.fmt_week( week_start, week_end )
  buf = String.new
  buf << "Week %02d" % week_start.cweek
  buf << " - #{week_start.strftime( "%a %b %-d")}"
  buf << " to #{week_end.strftime( "%a %b %-d %Y")}"
  buf
end

.main(args = ARGV) ⇒ Object



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
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
# File 'lib/footty/main.rb', line 6

def self.main( args=ARGV )
  puts banner # say hello


  opts = {  debug:   false,
            verbose: false,    ## add more details
            ## add cache/cache_dir - why? why not?

            query:   nil,

            ## display format/mode  - week/window/upcoming/past (default is today)
            yesterday: nil,
            tomorrow:  nil,
            upcoming:  nil,
            past:      nil,

            week:      false,
            #  window:    nil,   ## 2 day plus/minus  +2/-2
         }


  parser = OptionParser.new do |parser|
    parser.banner = "Usage: #{$PROGRAM_NAME} [options] LEAGUES"

    parser.on( "--verbose",
               "turn on verbose output (default: #{opts[:verbose]})" ) do |verbose|
      opts[:verbose] = true
    end

    parser.on( "-q NAME", "--query",
                 "query mode; display matches where team name matches query" ) do |query|
      opts[:query] = query
    end


    parser.on( "-y", "--yesterday" ) do |yesterday|
      opts[:yesterday] = true
    end
    parser.on( "-t", "--tomorrow" ) do |tomorrow|
      opts[:tomorrow] = true
    end
    parser.on( "-p", "--past" ) do |past|
      opts[:past] = true
    end
    parser.on( "-u", "--up", "--upcoming" ) do |upcoming|
      opts[:upcoming] = true
    end

    parser.on( "-w", "--week",
                "show matches of the (sport) week from tue to mon (default: #{opts[:week]})" ) do |week|
      opts[:week] = true
    end
  end
  parser.parse!( args )


  puts "OPTS:"
  p opts
  puts "ARGV:"
  p args


  ###
  ##   use simple norm(alize) args (that is,) league codes for now
  ##      - downcase, strip dot (.) etc.)
  ##   e.g.  en.facup   => enfacup
  ##         at.cup     => atcup     etc.
  args = args.map { |arg| arg.downcase.gsub( /[._-]/, '' ) }



  ######################
  ## note - first check for buil-in "magic" commands
  ##   e.g. leagues / codes    -  dump built-in league codes

  if args.include?( 'leagues' )
     puts "==> openfootball dataset sources:"
     pp OpenfootballDataset::SOURCES

     ## pretty print keys/codes only
     puts
     puts OpenfootballDataset::SOURCES.keys.join( ' ' )
     puts "   #{OpenfootballDataset::SOURCES.keys.size} league code(s)"

     exit 1
  end



  top = [['world',   '2026'],   ## world cup (w/ national teams)
       #  ['euro',   '2024'],
       #  ['mls',    '2025'],
       #  ['concacafcl', '2025'],
       #  ['mx',     '2024/25'],
         ['en',     '2025/26'],
         ['es',     '2025/26'],
         ['it',     '2025/26'],
         ['fr',     '2025/26'],
         ['de',     '2025/26'],
       #  ['decup',  '2025/26'],
       #  ['at',     '2025/26'],
       #  ['atcup',   '2025/26'],
         ['uefacl',   '2025/26'],
       #  ['uefael',   '2025/26'],
       #  ['uefaconf', '2025/26'],
         ['br',     '2026'],
         ['copa',   '2026'],       ## copa libertadores  (not copa america,etc.)
        ]


  leagues =  if args.size == 0
                 top
             else
                 ### auto-fill (latest) season/year
                 args.map do |arg|
                            [arg, OpenfootballDataset.latest_season( league: arg )]
                          end
             end


  ## fetch leagues
  datasets =  leagues.map do |league, season|
                              dataset = OpenfootballDataset.new( league: league, season: season )
                              ## parse matches
                              matches = dataset.matches
                              puts "  #{league} #{season} - #{matches.size} match(es)"
                              dataset
                          end



  ###################
  ##  check for query option to filter matches by query (team)
  if opts[:query]
    q = opts[:query]
    puts
    puts
    datasets.each do |dataset|
      matches = dataset.query( q )

      if matches.size == 0
         ## siltently skip for now
      else  ## assume matches found
        print "==> #{dataset.league_name}"
        print "   #{dataset.start_date} - #{dataset.end_date}"
        print "   -- #{dataset.matches.size} match(es)"
        print "\n"
        print_matches( matches )
      end
    end
    exit 1
  end


  # Dataset.new( league: 'euro', year: 2024 )
  # dataset = Dataset.new( league: league, year: year )

  ## in the future make today "configurable" as param - why? why not?
  today = Date.today


   what =  if opts[:yesterday]
             'yesterday'
           elsif opts[:tomorrow]
             'tomorrow'
           elsif opts[:past]
             'past'
           elsif opts[:upcoming]
             'upcoming'
           elsif opts[:week]
             'week'
           else
             'today'
           end


  ## if week get week number and start and end date (tuesday to mondey)
  if what == 'week'
    week_start, week_end = Footty.week_tue_to_mon( today)
    puts
    puts  "=== " + Footty.fmt_week( week_start, week_end ) + " ==="
  else
    ## start with two empty lines - assume (massive) debug output before ;-)
    puts
    puts
  end

  datasets.each do |dataset|
    print "==> #{dataset.league_name}"
    print "   #{dataset.start_date} - #{dataset.end_date}"
    print "   -- #{dataset.matches.size} match(es)"
    print "\n"

    if what == 'week'
      matches = dataset.weeks_matches( week_start, week_end )
      if matches.empty?
        puts (' '*4) + "** No matches scheduled or played in week #{week_start.cweek}.\n"
      end
   elsif what == 'yesterday'
      matches = dataset.yesterdays_matches
      if matches.empty?
         puts (' '*4) + "** No matches played yesterday.\n"
      end
    elsif what == 'tomorrow'
      matches = dataset.tomorrows_matches
      if matches.empty?
         puts (' '*4) + "** No matches scheduled tomorrow.\n"
      end
    elsif what == 'past'
      matches = dataset.past_matches
      if matches.empty?
         puts (' '*4) + "** No matches played yet.\n"
      end
    elsif what == 'upcoming'
      matches = dataset.upcoming_matches
      if matches.empty?
         puts (' '*4) + "** No more matches scheduled.\n"
      end
    else   ## assume today
       matches = dataset.todays_matches

       ## no matches today
       if matches.empty?
          puts (' '*4) + "** No matches scheduled today.\n"

          if opts[:verbose]
            ## note: was world cup 2018 - end date -- Date.new( 2018, 7, 11 )
            ## note: was euro 2020 (in 2021) - end date -- Date.new( 2021, 7, 11 )
            if Date.today > dataset.end_date    ## tournament is over, look back
              puts "Past matches:"
              matches = dataset.past_matches
            else  ## world cup is upcoming /in-progress,look forward
              puts "Upcoming matches:"
              matches = dataset.upcoming_matches( limit: 18 )
            end
          end
       end
     end
     print_matches( matches )
    end

end

note - assume “upstream” date is always date object or nil (NOT string)!!!



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
# File 'lib/footty/pp_matches.rb', line 8

def self.print_matches( matches )

  today = Date.today

  matches.each do |match|
    date = match['date']
    print "#{date.strftime('%a %b %d')} "      ## e.g. Thu Jun 14
    print "#{match['time']} "    if match['time']

    if date > today
       diff = (date - today).to_i
       print "%10s" % "(in #{diff}d) "
    end


    if match['team1'].is_a?( Hash )
      print "%22s" % "#{match['team1']['name']} (#{match['team1']['code']})"
    else
      print "%22s" % "#{match['team1']}"
    end



    if match['score'].is_a?( Hash )

        if match['score']['et']
           et = "#{match['score']['et'][0]}-#{match['score']['et'][1]} aet"
           print "  #{et}"
        end

        if match['score']['ft']
           ft = "#{match['score']['ft'][0]}-#{match['score']['ft'][1]}"
           if match['score']['et']
              print " (#{ft})"
           else
              print "  #{ft}"
           end
        end

        if match['score']['p']
          pen = "#{match['score']['p'][0]}-#{match['score']['p'][1]} pen"
          print ", #{pen}"
        end
        print "  "

    elsif match['score'] &&
          match['score'][0] && match['score'][1]
      score = "#{match['score'][0]}-#{match['score'][1]}"
      print "  #{score}  "
    else
      print "    vs    "
    end

    if match['team2'].is_a?( Hash )
      print "%-22s" % "#{match['team2']['name']} (#{match['team2']['code']})"
    else
      print "%-22s" % "#{match['team2']}"
    end


    print ""    ## note - add round marker!!


    print " #{match['group']} /"   if match['group']     ## (optional) group


    print " #{match['round']} "    ## knock out (k.o.) phase/stage


    print "%-5s " % "(\##{match['num']}) "   if match['num']

    print " @ #{match['ground']}"         if match['ground']


    print "\n"


    if match['goals1'] && match['goals2']
      print "                     ("

      print _pp_goals(match['goals1'])   if match['goals1'].size > 0

      print "; "   if match['goals1'].size > 0 &&
                      match['goals2'].size > 0

      print _pp_goals(match['goals2'])   if match['goals2'].size > 0

      print ")\n"
    end
  end
end

.rootObject



9
10
11
# File 'lib/footty/version.rb', line 9

def self.root
  File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
end

.week_tue_to_mon(today = Date.today) ⇒ Object



5
6
7
8
9
10
11
12
# File 'lib/footty/pp_week.rb', line 5

def self.week_tue_to_mon( today=Date.today )
  ## Calculate the start of the (sport) week (tuesday)
  ##  note - wday starts counting sunday (0), monday (1), etc.
  week_tue = today - (today.wday - 2) % 7
  week_mon = week_tue + 6

  [week_tue,week_mon]
end