footty 2025.5.2 → 2026.5.25
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +5 -3
- data/README.md +12 -13
- data/Rakefile +1 -1
- data/lib/footty/{dataset.rb → dataset/dataset.rb} +36 -46
- data/lib/footty/{openfootball.rb → dataset/openfootball.rb} +30 -13
- data/lib/footty/main.rb +248 -0
- data/lib/footty/pp_matches.rb +135 -0
- data/lib/footty/pp_week.rb +24 -0
- data/lib/footty/version.rb +1 -3
- data/lib/footty.rb +11 -272
- metadata +9 -7
- data/lib/footty/print.rb +0 -102
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 75b4ca658d25b404bf5b4f1594da50c42564ad7ffc43e3190429a7c4627b8c94
|
|
4
|
+
data.tar.gz: 25eac464226fea4b31da270bc195c443e34bc4fa68ce9de1bd0646b8ec63639e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1762d1389a13f0380267a7b8a0621b6df1e176eb847a4cac454fcfdfb87ece7321a950e80ad2c1ffedbd38f2966def3c63cda9d159063e568b1854bac0349b9
|
|
7
|
+
data.tar.gz: e791218fe6679d8c03545114cb60a7ce588b8f8dce4f8d80764aaa1da1168f6ab7552af00098ad6165a59d97ecea5fc1292cbb979689835f029bb4d06a1bc5cb
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
|
@@ -5,7 +5,9 @@ Rakefile
|
|
|
5
5
|
bin/footty
|
|
6
6
|
bin/ftty
|
|
7
7
|
lib/footty.rb
|
|
8
|
-
lib/footty/dataset.rb
|
|
9
|
-
lib/footty/openfootball.rb
|
|
10
|
-
lib/footty/
|
|
8
|
+
lib/footty/dataset/dataset.rb
|
|
9
|
+
lib/footty/dataset/openfootball.rb
|
|
10
|
+
lib/footty/main.rb
|
|
11
|
+
lib/footty/pp_matches.rb
|
|
12
|
+
lib/footty/pp_week.rb
|
|
11
13
|
lib/footty/version.rb
|
data/README.md
CHANGED
|
@@ -16,22 +16,22 @@ The footty (or ftty) command line tool lets you query the online football.db via
|
|
|
16
16
|
for upcoming or past matches. For example:
|
|
17
17
|
|
|
18
18
|
$ footty # Defaults to today's matches of top leagues
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
|
|
21
21
|
prints on Sep 27, 2024:
|
|
22
22
|
|
|
23
23
|
==> English Premier League 2024/25
|
|
24
24
|
Upcoming matches:
|
|
25
|
-
Sat Sep
|
|
26
|
-
Sat Sep
|
|
27
|
-
Sat Sep
|
|
28
|
-
Sat Sep
|
|
29
|
-
Sat Sep
|
|
30
|
-
Sat Sep
|
|
31
|
-
Sat Sep
|
|
32
|
-
Sun Sep
|
|
33
|
-
Sun Sep
|
|
34
|
-
Mon Sep
|
|
25
|
+
Sat Sep 28 12:30 (in 1d) Newcastle United FC vs Manchester City FC ▪ Matchday 6
|
|
26
|
+
Sat Sep 28 15:00 (in 1d) Arsenal FC vs Leicester City FC ▪ Matchday 6
|
|
27
|
+
Sat Sep 28 15:00 (in 1d) Brentford FC vs West Ham United FC ▪ Matchday 6
|
|
28
|
+
Sat Sep 28 15:00 (in 1d) Chelsea FC vs Brighton & Hove Albion ▪ Matchday 6
|
|
29
|
+
Sat Sep 28 15:00 (in 1d) Everton FC vs Crystal Palace FC ▪ Matchday 6
|
|
30
|
+
Sat Sep 28 15:00 (in 1d) Nottingham Forest FC vs Fulham FC ▪ Matchday 6
|
|
31
|
+
Sat Sep 28 17:30 (in 1d) Wolverhampton Wanderers FC vs Liverpool FC ▪ Matchday 6
|
|
32
|
+
Sun Sep 29 14:00 (in 2d) Ipswich Town FC vs Aston Villa FC ▪ Matchday 6
|
|
33
|
+
Sun Sep 29 16:30 (in 2d) Manchester United FC vs Tottenham Hotspur FC ▪ Matchday 6
|
|
34
|
+
Mon Sep 30 20:00 (in 3d) AFC Bournemouth vs Southampton FC ▪ Matchday 6
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
|
|
@@ -81,7 +81,7 @@ More
|
|
|
81
81
|
- `world` => World Cup
|
|
82
82
|
- `euro` => "Euro" - European Championship
|
|
83
83
|
|
|
84
|
-
See [footty/openfootball](https://github.com/sportdb/footty/blob/master/footty/lib/footty/openfootball.rb) for the complete built-in list of data sources (and league codes).
|
|
84
|
+
See [footty/openfootball](https://github.com/sportdb/footty/blob/master/footty/lib/footty/dataset/openfootball.rb) for the complete built-in list of data sources (and league codes).
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
|
|
@@ -110,4 +110,3 @@ Use it as you please with no restrictions whatsoever.
|
|
|
110
110
|
|
|
111
111
|
Yes, you can. More than welcome.
|
|
112
112
|
See [Help & Support »](https://github.com/openfootball/help)
|
|
113
|
-
|
data/Rakefile
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
module Footty
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
##
|
|
5
|
+
# note - assume "upstream" date is always date object or nil (NOT string)!!!
|
|
5
6
|
|
|
7
|
+
class Dataset
|
|
6
8
|
|
|
7
9
|
def matches
|
|
8
10
|
raise ArgumentError, "method matches must be implemented by concrete class!!"
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
def league_name
|
|
12
|
-
|
|
14
|
+
raise ArgumentError, "method league_name must be implemented by concrete class!!"
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
|
|
16
19
|
def end_date
|
|
17
20
|
@end_date ||= begin
|
|
18
21
|
end_date = nil
|
|
19
22
|
matches.each do |match|
|
|
20
|
-
|
|
21
|
-
end_date = date if end_date.nil? ||
|
|
22
|
-
date > end_date
|
|
23
|
+
end_date = match['date'] if end_date.nil? || match['date'] > end_date
|
|
23
24
|
end
|
|
24
25
|
end_date
|
|
25
26
|
end
|
|
@@ -29,58 +30,29 @@ module Footty
|
|
|
29
30
|
@start_date ||= begin
|
|
30
31
|
start_date = nil
|
|
31
32
|
matches.each do |match|
|
|
32
|
-
date =
|
|
33
|
-
start_date = date if start_date.nil? ||
|
|
34
|
-
date < start_date
|
|
33
|
+
start_date = date = match['date'] if start_date.nil? || date = match['date'] < start_date
|
|
35
34
|
end
|
|
36
35
|
start_date
|
|
37
36
|
end
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
def todays_matches( date: Date.today ) matches_for( date ); end
|
|
41
|
-
def tomorrows_matches( date: Date.today ) matches_for( date+1 ); end
|
|
42
|
-
def yesterdays_matches( date: Date.today ) matches_for( date-1 ); end
|
|
43
39
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
-
matches
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def weeks_matches( week_start, week_end ) matches_within( week_start, week_end); end
|
|
53
|
-
|
|
54
|
-
def matches_within( start_date, end_date )
|
|
55
|
-
matches = select_matches do |match|
|
|
56
|
-
date = Date.parse( match['date'] )
|
|
57
|
-
date >= start_date && date <= end_date
|
|
58
|
-
end
|
|
59
|
-
matches
|
|
60
|
-
end
|
|
40
|
+
def todays_matches( date: Date.today ) _select_matches { |match| date == match['date'] }; end
|
|
41
|
+
def tomorrows_matches( date: Date.today ) _select_matches { |match| date+1 == match['date'] }; end
|
|
42
|
+
def yesterdays_matches( date: Date.today ) _select_matches { |match| date-1 == match['date'] }; end
|
|
61
43
|
|
|
62
44
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
matches = select_matches do |match|
|
|
68
|
-
if rx.match( match['team1'] ) ||
|
|
69
|
-
rx.match( match['team2'] )
|
|
70
|
-
true
|
|
71
|
-
else
|
|
72
|
-
false
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
matches
|
|
45
|
+
def weeks_matches( start_week, end_week )
|
|
46
|
+
_select_matches do |match|
|
|
47
|
+
match['date'] >= start_week && match['date'] <= end_week
|
|
48
|
+
end
|
|
76
49
|
end
|
|
77
50
|
|
|
78
51
|
|
|
79
|
-
|
|
80
52
|
def upcoming_matches( date: Date.today,
|
|
81
53
|
limit: nil )
|
|
82
54
|
## note: includes todays matches for now
|
|
83
|
-
matches =
|
|
55
|
+
matches = _select_matches { |match| date <= match['date'] }
|
|
84
56
|
|
|
85
57
|
if limit
|
|
86
58
|
matches[0, limit] ## cut-off
|
|
@@ -89,16 +61,34 @@ module Footty
|
|
|
89
61
|
end
|
|
90
62
|
end
|
|
91
63
|
|
|
64
|
+
|
|
92
65
|
def past_matches( date: Date.today )
|
|
93
|
-
matches =
|
|
66
|
+
matches = _select_matches { |match| date > match['date'] }
|
|
94
67
|
## note reveserve matches (chronological order/last first)
|
|
95
68
|
## matches.reverse
|
|
96
69
|
matches
|
|
97
70
|
end
|
|
98
71
|
|
|
99
72
|
|
|
100
|
-
|
|
101
|
-
def
|
|
73
|
+
|
|
74
|
+
def query( q )
|
|
75
|
+
## query/check for team name match for now
|
|
76
|
+
rx = /#{Regexp.escape(q)}/i ## use case-insensitive regex match
|
|
77
|
+
|
|
78
|
+
_select_matches do |match|
|
|
79
|
+
if rx.match( match['team1'] ) ||
|
|
80
|
+
rx.match( match['team2'] )
|
|
81
|
+
true
|
|
82
|
+
else
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _select_matches( &blk )
|
|
102
92
|
selected = []
|
|
103
93
|
matches.each do |match|
|
|
104
94
|
selected << match if blk.call( match )
|
|
@@ -3,7 +3,9 @@ module Footty
|
|
|
3
3
|
|
|
4
4
|
class OpenfootballDataset < Dataset
|
|
5
5
|
SOURCES = {
|
|
6
|
-
'world' => { '
|
|
6
|
+
'world' => { '2026' => [ 'worldcup/2026--usa/cup.txt',
|
|
7
|
+
'worldcup/2026--usa/cup_finals.txt'],
|
|
8
|
+
'2022' => [ 'worldcup/2022--qatar/cup.txt',
|
|
7
9
|
'worldcup/2022--qatar/cup_finals.txt'],
|
|
8
10
|
'2018' => [ 'worldcup/2018--russia/cup.txt',
|
|
9
11
|
'worldcup/2018--russia/cup_finals.txt']
|
|
@@ -15,7 +17,7 @@ module Footty
|
|
|
15
17
|
'de2' => 'deutschland/$season$/2-bundesliga2.txt',
|
|
16
18
|
'de3' => 'deutschland/$season$/3-liga3.txt',
|
|
17
19
|
'decup' => 'deutschland/$season$/cup.txt',
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
'en' => 'england/$season$/1-premierleague.txt',
|
|
21
23
|
'en2' => 'england/$season$/2-championship.txt',
|
|
@@ -23,13 +25,13 @@ module Footty
|
|
|
23
25
|
## use eflcup, facup - why? why not?
|
|
24
26
|
'eneflcup' => 'england/$season$/eflcup.txt',
|
|
25
27
|
'enfacup' => 'england/$season$/facup.txt',
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
'es' => 'espana/$season$/1-liga.txt',
|
|
29
31
|
'escup' => 'espana/$season$/cup.txt',
|
|
30
32
|
|
|
31
33
|
'it'=> 'italy/$season$/1-seriea.txt',
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
'at'=> 'austria/$season$/1-bundesliga.txt',
|
|
34
36
|
'at2' => 'austria/$season$/2-liga2.txt',
|
|
35
37
|
'at3o' => 'austria/$season$/3-regionalliga-ost.txt',
|
|
@@ -48,7 +50,7 @@ module Footty
|
|
|
48
50
|
'br' => 'south-america/brazil/$year$_br1.txt',
|
|
49
51
|
'ar' => 'south-america/argentina/$year$_ar1.txt',
|
|
50
52
|
'co' => 'south-america/colombia/$year$_co1.txt',
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
## use a different code for copa libertadores? why? why not?
|
|
53
55
|
'copa' => 'south-america/copa-libertadores/$year$_copal.txt',
|
|
54
56
|
|
|
@@ -80,10 +82,10 @@ module Footty
|
|
|
80
82
|
|
|
81
83
|
## todo/fix - report error if no spec found
|
|
82
84
|
season = if spec.is_a?( Hash ) ## assume lookup by year
|
|
83
|
-
spec.keys[0]
|
|
85
|
+
spec.keys[0]
|
|
84
86
|
else ## assume vanilla urls (no lookup by year)
|
|
85
|
-
## default to
|
|
86
|
-
spec.index( '$year$') ? '
|
|
87
|
+
## default to 2026 or 2025/26 for now
|
|
88
|
+
spec.index( '$year$') ? '2026' : '2025/26'
|
|
87
89
|
end
|
|
88
90
|
season
|
|
89
91
|
end
|
|
@@ -116,13 +118,27 @@ module Footty
|
|
|
116
118
|
end
|
|
117
119
|
|
|
118
120
|
matches = matches.map {|match| match.as_json } # convert to json
|
|
119
|
-
|
|
120
|
-
## note - sort by date/time
|
|
121
|
+
|
|
122
|
+
## note - sort by date/time
|
|
121
123
|
## (assume stable sort; no reshuffle of matches if already sorted by date/time)
|
|
122
124
|
|
|
125
|
+
|
|
126
|
+
###
|
|
127
|
+
## note - convert date to date obj or nil !!!
|
|
128
|
+
matches = matches.map do |match|
|
|
129
|
+
if match['date'] && match['date'].empty? == false
|
|
130
|
+
match['date'] = Date.strptime( match['date'], '%Y-%m-%d' )
|
|
131
|
+
else
|
|
132
|
+
match['date'] = nil
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
match
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
123
139
|
matches = matches.sort do |l,r|
|
|
124
140
|
result = l['date'] <=> r['date']
|
|
125
|
-
result = l['time'] <=> r['time'] if result == 0 &&
|
|
141
|
+
result = l['time'] <=> r['time'] if result == 0 &&
|
|
126
142
|
(l['time'] && r['time'])
|
|
127
143
|
result
|
|
128
144
|
end
|
|
@@ -131,12 +147,13 @@ module Footty
|
|
|
131
147
|
end
|
|
132
148
|
|
|
133
149
|
|
|
150
|
+
|
|
134
151
|
def openfootball_url( path, season: )
|
|
135
152
|
repo, local_path = path.split( '/', 2)
|
|
136
153
|
url = "https://raw.githubusercontent.com/openfootball/#{repo}/master/#{local_path}"
|
|
137
154
|
## check for template vars too
|
|
138
155
|
season = Season( season )
|
|
139
|
-
url = url.gsub( '$year$',
|
|
156
|
+
url = url.gsub( '$year$', season.start_year.to_s )
|
|
140
157
|
url = url.gsub( '$season$', season.to_path )
|
|
141
158
|
url
|
|
142
159
|
end
|
|
@@ -144,7 +161,7 @@ module Footty
|
|
|
144
161
|
|
|
145
162
|
def matches() @matches; end
|
|
146
163
|
def league_name() @league_name; end
|
|
147
|
-
|
|
164
|
+
|
|
148
165
|
|
|
149
166
|
|
|
150
167
|
def get!( url )
|
data/lib/footty/main.rb
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module Footty
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def self.main( args=ARGV )
|
|
7
|
+
puts banner # say hello
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
opts = { debug: false,
|
|
11
|
+
verbose: false, ## add more details
|
|
12
|
+
## add cache/cache_dir - why? why not?
|
|
13
|
+
|
|
14
|
+
query: nil,
|
|
15
|
+
|
|
16
|
+
## display format/mode - week/window/upcoming/past (default is today)
|
|
17
|
+
yesterday: nil,
|
|
18
|
+
tomorrow: nil,
|
|
19
|
+
upcoming: nil,
|
|
20
|
+
past: nil,
|
|
21
|
+
|
|
22
|
+
week: false,
|
|
23
|
+
# window: nil, ## 2 day plus/minus +2/-2
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
parser = OptionParser.new do |parser|
|
|
28
|
+
parser.banner = "Usage: #{$PROGRAM_NAME} [options] LEAGUES"
|
|
29
|
+
|
|
30
|
+
parser.on( "--verbose",
|
|
31
|
+
"turn on verbose output (default: #{opts[:verbose]})" ) do |verbose|
|
|
32
|
+
opts[:verbose] = true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
parser.on( "-q NAME", "--query",
|
|
36
|
+
"query mode; display matches where team name matches query" ) do |query|
|
|
37
|
+
opts[:query] = query
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
parser.on( "-y", "--yesterday" ) do |yesterday|
|
|
42
|
+
opts[:yesterday] = true
|
|
43
|
+
end
|
|
44
|
+
parser.on( "-t", "--tomorrow" ) do |tomorrow|
|
|
45
|
+
opts[:tomorrow] = true
|
|
46
|
+
end
|
|
47
|
+
parser.on( "-p", "--past" ) do |past|
|
|
48
|
+
opts[:past] = true
|
|
49
|
+
end
|
|
50
|
+
parser.on( "-u", "--up", "--upcoming" ) do |upcoming|
|
|
51
|
+
opts[:upcoming] = true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
parser.on( "-w", "--week",
|
|
55
|
+
"show matches of the (sport) week from tue to mon (default: #{opts[:week]})" ) do |week|
|
|
56
|
+
opts[:week] = true
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
parser.parse!( args )
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
puts "OPTS:"
|
|
63
|
+
p opts
|
|
64
|
+
puts "ARGV:"
|
|
65
|
+
p args
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
###
|
|
69
|
+
## use simple norm(alize) args (that is,) league codes for now
|
|
70
|
+
## - downcase, strip dot (.) etc.)
|
|
71
|
+
## e.g. en.facup => enfacup
|
|
72
|
+
## at.cup => atcup etc.
|
|
73
|
+
args = args.map { |arg| arg.downcase.gsub( /[._-]/, '' ) }
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
######################
|
|
78
|
+
## note - first check for buil-in "magic" commands
|
|
79
|
+
## e.g. leagues / codes - dump built-in league codes
|
|
80
|
+
|
|
81
|
+
if args.include?( 'leagues' )
|
|
82
|
+
puts "==> openfootball dataset sources:"
|
|
83
|
+
pp OpenfootballDataset::SOURCES
|
|
84
|
+
|
|
85
|
+
## pretty print keys/codes only
|
|
86
|
+
puts
|
|
87
|
+
puts OpenfootballDataset::SOURCES.keys.join( ' ' )
|
|
88
|
+
puts " #{OpenfootballDataset::SOURCES.keys.size} league code(s)"
|
|
89
|
+
|
|
90
|
+
exit 1
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
top = [['world', '2026'], ## world cup (w/ national teams)
|
|
96
|
+
# ['euro', '2024'],
|
|
97
|
+
# ['mls', '2025'],
|
|
98
|
+
# ['concacafcl', '2025'],
|
|
99
|
+
# ['mx', '2024/25'],
|
|
100
|
+
['en', '2025/26'],
|
|
101
|
+
['es', '2025/26'],
|
|
102
|
+
['it', '2025/26'],
|
|
103
|
+
['fr', '2025/26'],
|
|
104
|
+
['de', '2025/26'],
|
|
105
|
+
# ['decup', '2025/26'],
|
|
106
|
+
# ['at', '2025/26'],
|
|
107
|
+
# ['atcup', '2025/26'],
|
|
108
|
+
['uefacl', '2025/26'],
|
|
109
|
+
# ['uefael', '2025/26'],
|
|
110
|
+
# ['uefaconf', '2025/26'],
|
|
111
|
+
['br', '2026'],
|
|
112
|
+
['copa', '2026'], ## copa libertadores (not copa america,etc.)
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
leagues = if args.size == 0
|
|
117
|
+
top
|
|
118
|
+
else
|
|
119
|
+
### auto-fill (latest) season/year
|
|
120
|
+
args.map do |arg|
|
|
121
|
+
[arg, OpenfootballDataset.latest_season( league: arg )]
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## fetch leagues
|
|
127
|
+
datasets = leagues.map do |league, season|
|
|
128
|
+
dataset = OpenfootballDataset.new( league: league, season: season )
|
|
129
|
+
## parse matches
|
|
130
|
+
matches = dataset.matches
|
|
131
|
+
puts " #{league} #{season} - #{matches.size} match(es)"
|
|
132
|
+
dataset
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
###################
|
|
138
|
+
## check for query option to filter matches by query (team)
|
|
139
|
+
if opts[:query]
|
|
140
|
+
q = opts[:query]
|
|
141
|
+
puts
|
|
142
|
+
puts
|
|
143
|
+
datasets.each do |dataset|
|
|
144
|
+
matches = dataset.query( q )
|
|
145
|
+
|
|
146
|
+
if matches.size == 0
|
|
147
|
+
## siltently skip for now
|
|
148
|
+
else ## assume matches found
|
|
149
|
+
print "==> #{dataset.league_name}"
|
|
150
|
+
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
151
|
+
print " -- #{dataset.matches.size} match(es)"
|
|
152
|
+
print "\n"
|
|
153
|
+
print_matches( matches )
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
exit 1
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# Dataset.new( league: 'euro', year: 2024 )
|
|
161
|
+
# dataset = Dataset.new( league: league, year: year )
|
|
162
|
+
|
|
163
|
+
## in the future make today "configurable" as param - why? why not?
|
|
164
|
+
today = Date.today
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
what = if opts[:yesterday]
|
|
168
|
+
'yesterday'
|
|
169
|
+
elsif opts[:tomorrow]
|
|
170
|
+
'tomorrow'
|
|
171
|
+
elsif opts[:past]
|
|
172
|
+
'past'
|
|
173
|
+
elsif opts[:upcoming]
|
|
174
|
+
'upcoming'
|
|
175
|
+
elsif opts[:week]
|
|
176
|
+
'week'
|
|
177
|
+
else
|
|
178
|
+
'today'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
## if week get week number and start and end date (tuesday to mondey)
|
|
183
|
+
if what == 'week'
|
|
184
|
+
week_start, week_end = Footty.week_tue_to_mon( today)
|
|
185
|
+
puts
|
|
186
|
+
puts "=== " + Footty.fmt_week( week_start, week_end ) + " ==="
|
|
187
|
+
else
|
|
188
|
+
## start with two empty lines - assume (massive) debug output before ;-)
|
|
189
|
+
puts
|
|
190
|
+
puts
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
datasets.each do |dataset|
|
|
194
|
+
print "==> #{dataset.league_name}"
|
|
195
|
+
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
196
|
+
print " -- #{dataset.matches.size} match(es)"
|
|
197
|
+
print "\n"
|
|
198
|
+
|
|
199
|
+
if what == 'week'
|
|
200
|
+
matches = dataset.weeks_matches( week_start, week_end )
|
|
201
|
+
if matches.empty?
|
|
202
|
+
puts (' '*4) + "** No matches scheduled or played in week #{week_start.cweek}.\n"
|
|
203
|
+
end
|
|
204
|
+
elsif what == 'yesterday'
|
|
205
|
+
matches = dataset.yesterdays_matches
|
|
206
|
+
if matches.empty?
|
|
207
|
+
puts (' '*4) + "** No matches played yesterday.\n"
|
|
208
|
+
end
|
|
209
|
+
elsif what == 'tomorrow'
|
|
210
|
+
matches = dataset.tomorrows_matches
|
|
211
|
+
if matches.empty?
|
|
212
|
+
puts (' '*4) + "** No matches scheduled tomorrow.\n"
|
|
213
|
+
end
|
|
214
|
+
elsif what == 'past'
|
|
215
|
+
matches = dataset.past_matches
|
|
216
|
+
if matches.empty?
|
|
217
|
+
puts (' '*4) + "** No matches played yet.\n"
|
|
218
|
+
end
|
|
219
|
+
elsif what == 'upcoming'
|
|
220
|
+
matches = dataset.upcoming_matches
|
|
221
|
+
if matches.empty?
|
|
222
|
+
puts (' '*4) + "** No more matches scheduled.\n"
|
|
223
|
+
end
|
|
224
|
+
else ## assume today
|
|
225
|
+
matches = dataset.todays_matches
|
|
226
|
+
|
|
227
|
+
## no matches today
|
|
228
|
+
if matches.empty?
|
|
229
|
+
puts (' '*4) + "** No matches scheduled today.\n"
|
|
230
|
+
|
|
231
|
+
if opts[:verbose]
|
|
232
|
+
## note: was world cup 2018 - end date -- Date.new( 2018, 7, 11 )
|
|
233
|
+
## note: was euro 2020 (in 2021) - end date -- Date.new( 2021, 7, 11 )
|
|
234
|
+
if Date.today > dataset.end_date ## tournament is over, look back
|
|
235
|
+
puts "Past matches:"
|
|
236
|
+
matches = dataset.past_matches
|
|
237
|
+
else ## world cup is upcoming /in-progress,look forward
|
|
238
|
+
puts "Upcoming matches:"
|
|
239
|
+
matches = dataset.upcoming_matches( limit: 18 )
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
print_matches( matches )
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
end # method self.main
|
|
248
|
+
end # module Footty
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
|
|
2
|
+
module Footty
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# note - assume "upstream" date is always date object or nil (NOT string)!!!
|
|
7
|
+
|
|
8
|
+
def self.print_matches( matches )
|
|
9
|
+
|
|
10
|
+
today = Date.today
|
|
11
|
+
|
|
12
|
+
matches.each do |match|
|
|
13
|
+
date = match['date']
|
|
14
|
+
print "#{date.strftime('%a %b %d')} " ## e.g. Thu Jun 14
|
|
15
|
+
print "#{match['time']} " if match['time']
|
|
16
|
+
|
|
17
|
+
if date > today
|
|
18
|
+
diff = (date - today).to_i
|
|
19
|
+
print "%10s" % "(in #{diff}d) "
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if match['team1'].is_a?( Hash )
|
|
24
|
+
print "%22s" % "#{match['team1']['name']} (#{match['team1']['code']})"
|
|
25
|
+
else
|
|
26
|
+
print "%22s" % "#{match['team1']}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if match['score'].is_a?( Hash )
|
|
32
|
+
|
|
33
|
+
if match['score']['et']
|
|
34
|
+
et = "#{match['score']['et'][0]}-#{match['score']['et'][1]} aet"
|
|
35
|
+
print " #{et}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if match['score']['ft']
|
|
39
|
+
ft = "#{match['score']['ft'][0]}-#{match['score']['ft'][1]}"
|
|
40
|
+
if match['score']['et']
|
|
41
|
+
print " (#{ft})"
|
|
42
|
+
else
|
|
43
|
+
print " #{ft}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if match['score']['p']
|
|
48
|
+
pen = "#{match['score']['p'][0]}-#{match['score']['p'][1]} pen"
|
|
49
|
+
print ", #{pen}"
|
|
50
|
+
end
|
|
51
|
+
print " "
|
|
52
|
+
|
|
53
|
+
elsif match['score'] &&
|
|
54
|
+
match['score'][0] && match['score'][1]
|
|
55
|
+
score = "#{match['score'][0]}-#{match['score'][1]}"
|
|
56
|
+
print " #{score} "
|
|
57
|
+
else
|
|
58
|
+
print " vs "
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if match['team2'].is_a?( Hash )
|
|
62
|
+
print "%-22s" % "#{match['team2']['name']} (#{match['team2']['code']})"
|
|
63
|
+
else
|
|
64
|
+
print "%-22s" % "#{match['team2']}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
print "▪" ## note - add round marker!!
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
print " #{match['group']} /" if match['group'] ## (optional) group
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
print " #{match['round']} " ## knock out (k.o.) phase/stage
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
print "%-5s " % "(\##{match['num']}) " if match['num']
|
|
78
|
+
|
|
79
|
+
print " @ #{match['ground']}" if match['ground']
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
print "\n"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if match['goals1'] && match['goals2']
|
|
86
|
+
print " ("
|
|
87
|
+
|
|
88
|
+
print _pp_goals(match['goals1']) if match['goals1'].size > 0
|
|
89
|
+
|
|
90
|
+
print "; " if match['goals1'].size > 0 &&
|
|
91
|
+
match['goals2'].size > 0
|
|
92
|
+
|
|
93
|
+
print _pp_goals(match['goals2']) if match['goals2'].size > 0
|
|
94
|
+
|
|
95
|
+
print ")\n"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def self._pp_goals( recs )
|
|
103
|
+
players = {}
|
|
104
|
+
|
|
105
|
+
## "fold" multiple goals of player
|
|
106
|
+
recs.each do |rec|
|
|
107
|
+
|
|
108
|
+
name = rec['name']
|
|
109
|
+
|
|
110
|
+
if rec['minute'].nil? || rec['minute'].empty?
|
|
111
|
+
puts "!! WARN - (goals) minute empty:"
|
|
112
|
+
pp rec
|
|
113
|
+
## use '??'
|
|
114
|
+
rec['minute'] = '??'
|
|
115
|
+
## raise ArgumentError, "minute empty"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
goal = String.new
|
|
119
|
+
goal << "#{rec['minute']}'"
|
|
120
|
+
goal << '(og)' if rec['owngoal'] == true
|
|
121
|
+
goal << '(p)' if rec['penalty'] == true
|
|
122
|
+
|
|
123
|
+
player_rec = players[ name ] ||= { name: name, goals: [] }
|
|
124
|
+
player_rec[:goals] << goal
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
buf = players.map do |_,player|
|
|
129
|
+
"#{player[:name]} #{player[:goals].join(',')}"
|
|
130
|
+
end.join( ' ' )
|
|
131
|
+
buf
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
end # module Footty
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
module Footty
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def self.week_tue_to_mon( today=Date.today )
|
|
6
|
+
## Calculate the start of the (sport) week (tuesday)
|
|
7
|
+
## note - wday starts counting sunday (0), monday (1), etc.
|
|
8
|
+
week_tue = today - (today.wday - 2) % 7
|
|
9
|
+
week_mon = week_tue + 6
|
|
10
|
+
|
|
11
|
+
[week_tue,week_mon]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def self.fmt_week( week_start, week_end )
|
|
16
|
+
buf = String.new
|
|
17
|
+
buf << "Week %02d" % week_start.cweek
|
|
18
|
+
buf << " - #{week_start.strftime( "%a %b %-d")}"
|
|
19
|
+
buf << " to #{week_end.strftime( "%a %b %-d %Y")}"
|
|
20
|
+
buf
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
end # module Footty
|
data/lib/footty/version.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
module Footty
|
|
3
|
-
VERSION = '
|
|
3
|
+
VERSION = '2026.5.25'
|
|
4
4
|
|
|
5
5
|
def self.banner
|
|
6
6
|
"footty/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
|
@@ -10,5 +10,3 @@ module Footty
|
|
|
10
10
|
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
|
-
|
|
14
|
-
|
data/lib/footty.rb
CHANGED
|
@@ -1,294 +1,33 @@
|
|
|
1
1
|
require 'sportdb/quick' ## note - pulls in cocos et al
|
|
2
|
-
require 'webget'
|
|
3
|
-
|
|
4
|
-
require 'optparse'
|
|
2
|
+
require 'webget' ## add webcache support
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
# our own code
|
|
10
8
|
require_relative 'footty/version' # let version always go first
|
|
11
|
-
require_relative 'footty/dataset'
|
|
12
|
-
require_relative 'footty/openfootball'
|
|
13
|
-
|
|
14
|
-
require_relative 'footty/print'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## set cache to local .cache dir for now - why? why not?
|
|
19
|
-
Webcache.root = './cache'
|
|
20
|
-
# pp Webcache.root
|
|
21
|
-
Webget.config.sleep = 1 ## set delay in secs (to 1 sec - default is/maybe 3)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
module Footty
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def self.week_tue_to_mon( today=Date.today )
|
|
28
|
-
## Calculate the start of the (sport) week (tuesday)
|
|
29
|
-
## note - wday starts counting sunday (0), monday (1), etc.
|
|
30
|
-
week_tue = today - (today.wday - 2) % 7
|
|
31
|
-
week_mon = week_tue + 6
|
|
32
|
-
|
|
33
|
-
[week_tue,week_mon]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def self.fmt_week( week_start, week_end )
|
|
37
|
-
buf = String.new
|
|
38
|
-
buf << "Week %02d" % week_start.cweek
|
|
39
|
-
buf << " - #{week_start.strftime( "%a %b/%-d")}"
|
|
40
|
-
buf << " to #{week_end.strftime( "%a %b/%-d %Y")}"
|
|
41
|
-
buf
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def self.main( args=ARGV )
|
|
47
|
-
puts banner # say hello
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
opts = { debug: false,
|
|
51
|
-
verbose: false, ## add more details
|
|
52
|
-
## add cache/cache_dir - why? why not?
|
|
53
|
-
|
|
54
|
-
query: nil,
|
|
55
|
-
|
|
56
|
-
## display format/mode - week/window/upcoming/past (default is today)
|
|
57
|
-
yesterday: nil,
|
|
58
|
-
tomorrow: nil,
|
|
59
|
-
upcoming: nil,
|
|
60
|
-
past: nil,
|
|
61
|
-
|
|
62
|
-
week: false,
|
|
63
|
-
# window: nil, ## 2 day plus/minus +2/-2
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
parser = OptionParser.new do |parser|
|
|
68
|
-
parser.banner = "Usage: #{$PROGRAM_NAME} [options] LEAGUES"
|
|
69
|
-
|
|
70
|
-
parser.on( "--verbose",
|
|
71
|
-
"turn on verbose output (default: #{opts[:verbose]})" ) do |verbose|
|
|
72
|
-
opts[:verbose] = true
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
parser.on( "-q NAME", "--query",
|
|
76
|
-
"query mode; display matches where team name matches query" ) do |query|
|
|
77
|
-
opts[:query] = query
|
|
78
|
-
end
|
|
79
9
|
|
|
10
|
+
require_relative 'footty/dataset/dataset'
|
|
11
|
+
require_relative 'footty/dataset/openfootball'
|
|
80
12
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
parser.on( "-t", "--tomorrow" ) do |tomorrow|
|
|
85
|
-
opts[:tomorrow] = true
|
|
86
|
-
end
|
|
87
|
-
parser.on( "-p", "--past" ) do |past|
|
|
88
|
-
opts[:past] = true
|
|
89
|
-
end
|
|
90
|
-
parser.on( "-u", "--up", "--upcoming" ) do |upcoming|
|
|
91
|
-
opts[:upcoming] = true
|
|
92
|
-
end
|
|
13
|
+
require_relative 'footty/pp_matches'
|
|
14
|
+
require_relative 'footty/pp_week'
|
|
93
15
|
|
|
94
|
-
|
|
95
|
-
"show matches of the (sport) week from tue to mon (default: #{opts[:week]})" ) do |week|
|
|
96
|
-
opts[:week] = true
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
parser.parse!( args )
|
|
16
|
+
require_relative 'footty/main'
|
|
100
17
|
|
|
101
18
|
|
|
102
|
-
puts "OPTS:"
|
|
103
|
-
p opts
|
|
104
|
-
puts "ARGV:"
|
|
105
|
-
p args
|
|
106
19
|
|
|
107
20
|
|
|
108
|
-
###
|
|
109
|
-
## use simple norm(alize) args (that is,) league codes for now
|
|
110
|
-
## - downcase, strip dot (.) etc.)
|
|
111
|
-
## e.g. en.facup => enfacup
|
|
112
|
-
## at.cup => atcup etc.
|
|
113
|
-
args = args.map { |arg| arg.downcase.gsub( /[._-]/, '' ) }
|
|
114
21
|
|
|
22
|
+
## set cache to local .cache dir for now - why? why not?
|
|
23
|
+
Webcache.root = './cache'
|
|
115
24
|
|
|
25
|
+
# pp Webcache.root
|
|
26
|
+
Webget.config.sleep = 1 ## set delay in secs (to 1 sec - default is/maybe 3)
|
|
116
27
|
|
|
117
|
-
######################
|
|
118
|
-
## note - first check for buil-in "magic" commands
|
|
119
|
-
## e.g. leagues / codes - dump built-in league codes
|
|
120
|
-
|
|
121
|
-
if args.include?( 'leagues' )
|
|
122
|
-
puts "==> openfootball dataset sources:"
|
|
123
|
-
pp OpenfootballDataset::SOURCES
|
|
124
|
-
|
|
125
|
-
## pretty print keys/codes only
|
|
126
|
-
puts
|
|
127
|
-
puts OpenfootballDataset::SOURCES.keys.join( ' ' )
|
|
128
|
-
puts " #{OpenfootballDataset::SOURCES.keys.size} league code(s)"
|
|
129
|
-
|
|
130
|
-
exit 1
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
top = [['world', '2022'],
|
|
138
|
-
['euro', '2024'],
|
|
139
|
-
['mls', '2025'],
|
|
140
|
-
['concacafcl', '2025'],
|
|
141
|
-
['mx', '2024/25'],
|
|
142
|
-
['copa', '2025'], ## copa libertadores
|
|
143
|
-
['en', '2024/25'],
|
|
144
|
-
['es', '2024/25'],
|
|
145
|
-
['it', '2024/25'],
|
|
146
|
-
['fr', '2024/25'],
|
|
147
|
-
['de', '2024/25'],
|
|
148
|
-
['decup', '2024/25'],
|
|
149
|
-
['at', '2024/25'],
|
|
150
|
-
['atcup', '2024/25'],
|
|
151
|
-
['uefacl', '2024/25'],
|
|
152
|
-
['uefael', '2024/25'],
|
|
153
|
-
['uefaconf', '2024/25'],
|
|
154
|
-
]
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
leagues = if args.size == 0
|
|
158
|
-
top
|
|
159
|
-
else
|
|
160
|
-
### auto-fill (latest) season/year
|
|
161
|
-
args.map do |arg|
|
|
162
|
-
[arg, OpenfootballDataset.latest_season( league: arg )]
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
## fetch leagues
|
|
168
|
-
datasets = leagues.map do |league, season|
|
|
169
|
-
dataset = OpenfootballDataset.new( league: league, season: season )
|
|
170
|
-
## parse matches
|
|
171
|
-
matches = dataset.matches
|
|
172
|
-
puts " #{league} #{season} - #{matches.size} match(es)"
|
|
173
|
-
dataset
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
###################
|
|
179
|
-
## check for query option to filter matches by query (team)
|
|
180
|
-
if opts[:query]
|
|
181
|
-
q = opts[:query]
|
|
182
|
-
puts
|
|
183
|
-
puts
|
|
184
|
-
datasets.each do |dataset|
|
|
185
|
-
matches = dataset.query( q )
|
|
186
|
-
|
|
187
|
-
if matches.size == 0
|
|
188
|
-
## siltently skip for now
|
|
189
|
-
else ## assume matches found
|
|
190
|
-
print "==> #{dataset.league_name}"
|
|
191
|
-
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
192
|
-
print " -- #{dataset.matches.size} match(es)"
|
|
193
|
-
print "\n"
|
|
194
|
-
print_matches( matches )
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
exit 1
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
# Dataset.new( league: 'euro', year: 2024 )
|
|
202
|
-
# dataset = Dataset.new( league: league, year: year )
|
|
203
|
-
|
|
204
|
-
## in the future make today "configurable" as param - why? why not?
|
|
205
|
-
today = Date.today
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
what = if opts[:yesterday]
|
|
209
|
-
'yesterday'
|
|
210
|
-
elsif opts[:tomorrow]
|
|
211
|
-
'tomorrow'
|
|
212
|
-
elsif opts[:past]
|
|
213
|
-
'past'
|
|
214
|
-
elsif opts[:upcoming]
|
|
215
|
-
'upcoming'
|
|
216
|
-
elsif opts[:week]
|
|
217
|
-
'week'
|
|
218
|
-
else
|
|
219
|
-
'today'
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
## if week get week number and start and end date (tuesday to mondey)
|
|
224
|
-
if what == 'week'
|
|
225
|
-
week_start, week_end = Footty.week_tue_to_mon( today)
|
|
226
|
-
puts
|
|
227
|
-
puts "=== " + Footty.fmt_week( week_start, week_end ) + " ==="
|
|
228
|
-
else
|
|
229
|
-
## start with two empty lines - assume (massive) debug output before ;-)
|
|
230
|
-
puts
|
|
231
|
-
puts
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
datasets.each do |dataset|
|
|
235
|
-
print "==> #{dataset.league_name}"
|
|
236
|
-
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
237
|
-
print " -- #{dataset.matches.size} match(es)"
|
|
238
|
-
print "\n"
|
|
239
|
-
|
|
240
|
-
if what == 'week'
|
|
241
|
-
matches = dataset.weeks_matches( week_start, week_end )
|
|
242
|
-
if matches.empty?
|
|
243
|
-
puts (' '*4) + "** No matches scheduled or played in week #{week_start.cweek}.\n"
|
|
244
|
-
end
|
|
245
|
-
elsif what == 'yesterday'
|
|
246
|
-
matches = dataset.yesterdays_matches
|
|
247
|
-
if matches.empty?
|
|
248
|
-
puts (' '*4) + "** No matches played yesterday.\n"
|
|
249
|
-
end
|
|
250
|
-
elsif what == 'tomorrow'
|
|
251
|
-
matches = dataset.tomorrows_matches
|
|
252
|
-
if matches.empty?
|
|
253
|
-
puts (' '*4) + "** No matches scheduled tomorrow.\n"
|
|
254
|
-
end
|
|
255
|
-
elsif what == 'past'
|
|
256
|
-
matches = dataset.past_matches
|
|
257
|
-
if matches.empty?
|
|
258
|
-
puts (' '*4) + "** No matches played yet.\n"
|
|
259
|
-
end
|
|
260
|
-
elsif what == 'upcoming'
|
|
261
|
-
matches = dataset.upcoming_matches
|
|
262
|
-
if matches.empty?
|
|
263
|
-
puts (' '*4) + "** No more matches scheduled.\n"
|
|
264
|
-
end
|
|
265
|
-
else ## assume today
|
|
266
|
-
matches = dataset.todays_matches
|
|
267
|
-
|
|
268
|
-
## no matches today
|
|
269
|
-
if matches.empty?
|
|
270
|
-
puts (' '*4) + "** No matches scheduled today.\n"
|
|
271
|
-
|
|
272
|
-
if opts[:verbose]
|
|
273
|
-
## note: was world cup 2018 - end date -- Date.new( 2018, 7, 11 )
|
|
274
|
-
## note: was euro 2020 (in 2021) - end date -- Date.new( 2021, 7, 11 )
|
|
275
|
-
if Date.today > dataset.end_date ## tournament is over, look back
|
|
276
|
-
puts "Past matches:"
|
|
277
|
-
matches = dataset.past_matches
|
|
278
|
-
else ## world cup is upcoming /in-progress,look forward
|
|
279
|
-
puts "Upcoming matches:"
|
|
280
|
-
matches = dataset.upcoming_matches( limit: 18 )
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
print_matches( matches )
|
|
286
|
-
end
|
|
287
28
|
|
|
288
|
-
end # method self.main
|
|
289
|
-
end # module Footty
|
|
290
29
|
|
|
291
30
|
|
|
292
31
|
|
|
293
32
|
|
|
294
|
-
Footty.main
|
|
33
|
+
Footty.main if __FILE__ == $0
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: footty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2026.5.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gerald Bauer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sportdb-quick
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.7.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.7.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: webget
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -91,9 +91,11 @@ files:
|
|
|
91
91
|
- bin/footty
|
|
92
92
|
- bin/ftty
|
|
93
93
|
- lib/footty.rb
|
|
94
|
-
- lib/footty/dataset.rb
|
|
95
|
-
- lib/footty/openfootball.rb
|
|
96
|
-
- lib/footty/
|
|
94
|
+
- lib/footty/dataset/dataset.rb
|
|
95
|
+
- lib/footty/dataset/openfootball.rb
|
|
96
|
+
- lib/footty/main.rb
|
|
97
|
+
- lib/footty/pp_matches.rb
|
|
98
|
+
- lib/footty/pp_week.rb
|
|
97
99
|
- lib/footty/version.rb
|
|
98
100
|
homepage: https://github.com/sportdb/footty
|
|
99
101
|
licenses:
|
data/lib/footty/print.rb
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module Footty
|
|
3
|
-
|
|
4
|
-
def self.print_matches( matches )
|
|
5
|
-
|
|
6
|
-
today = Date.today
|
|
7
|
-
|
|
8
|
-
matches.each do |match|
|
|
9
|
-
print " %5s" % "\##{match['num']} " if match['num']
|
|
10
|
-
|
|
11
|
-
date = Date.strptime( match['date'], '%Y-%m-%d' )
|
|
12
|
-
print "#{date.strftime('%a %b/%d')} " ## e.g. Thu Jun/14
|
|
13
|
-
print "#{match['time']} " if match['time']
|
|
14
|
-
|
|
15
|
-
if date > today
|
|
16
|
-
diff = (date - today).to_i
|
|
17
|
-
print "%10s" % "(in #{diff}d) "
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if match['team1'].is_a?( Hash )
|
|
22
|
-
print "%22s" % "#{match['team1']['name']} (#{match['team1']['code']})"
|
|
23
|
-
else
|
|
24
|
-
print "%22s" % "#{match['team1']}"
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if match['score'].is_a?( Hash ) &&
|
|
29
|
-
match['score']['ft']
|
|
30
|
-
if match['score']['ft']
|
|
31
|
-
print " #{match['score']['ft'][0]}-#{match['score']['ft'][1]} "
|
|
32
|
-
end
|
|
33
|
-
if match['score']['et']
|
|
34
|
-
print "aet #{match['score']['et'][0]}-#{match['score']['et'][1]} "
|
|
35
|
-
end
|
|
36
|
-
if match['score']['p']
|
|
37
|
-
print "pen #{match['score']['p'][0]}-#{match['score']['p'][1]} "
|
|
38
|
-
end
|
|
39
|
-
elsif match['score1'] && match['score2']
|
|
40
|
-
## todo/fix: add support for knockout scores
|
|
41
|
-
## with score1et/score1p (extra time and penalty)
|
|
42
|
-
print " #{match['score1']}-#{match['score2']} "
|
|
43
|
-
print "(#{match['score1i']}-#{match['score2i']}) "
|
|
44
|
-
else
|
|
45
|
-
print " vs "
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
if match['team2'].is_a?( Hash )
|
|
49
|
-
print "%-22s" % "#{match['team2']['name']} (#{match['team2']['code']})"
|
|
50
|
-
else
|
|
51
|
-
print "%-22s" % "#{match['team2']}"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
if match['stage']
|
|
55
|
-
print " #{match['stage']} /" ## stage
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
if match['group']
|
|
59
|
-
print " #{match['group']} /" ## group phase
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
print " #{match['round']} " ## knock out (k.o.) phase/stage
|
|
63
|
-
|
|
64
|
-
## todo/fix - check for ground name in use???
|
|
65
|
-
if match['stadium']
|
|
66
|
-
print " @ #{match['stadium']['name']}, #{match['city']}"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
print "\n"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if match['goals1'] && match['goals2']
|
|
73
|
-
print " ["
|
|
74
|
-
match['goals1'].each_with_index do |goal,i|
|
|
75
|
-
print " " if i > 0
|
|
76
|
-
print "#{goal['name']}"
|
|
77
|
-
print " #{goal['minute']}"
|
|
78
|
-
print "+#{goal['offset']}" if goal['offset']
|
|
79
|
-
print "'"
|
|
80
|
-
print " (o.g.)" if goal['owngoal']
|
|
81
|
-
print " (pen.)" if goal['penalty']
|
|
82
|
-
end
|
|
83
|
-
match['goals2'].each_with_index do |goal,i|
|
|
84
|
-
if i == 0
|
|
85
|
-
print "; "
|
|
86
|
-
else
|
|
87
|
-
print " "
|
|
88
|
-
end
|
|
89
|
-
print "#{goal['name']}"
|
|
90
|
-
print " #{goal['minute']}"
|
|
91
|
-
print "+#{goal['offset']}" if goal['offset']
|
|
92
|
-
print "'"
|
|
93
|
-
print " (o.g.)" if goal['owngoal']
|
|
94
|
-
print " (pen.)" if goal['penalty']
|
|
95
|
-
end
|
|
96
|
-
print "]\n"
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end # module Footty
|