footty 2025.4.28 → 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 -33
- data/lib/footty/{openfootball.rb → dataset/openfootball.rb} +37 -14
- 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 +16 -230
- 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,45 +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
|
|
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
|
|
48
43
|
|
|
49
44
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
matches = select_matches do |match|
|
|
55
|
-
if rx.match( match['team1'] ) ||
|
|
56
|
-
rx.match( match['team2'] )
|
|
57
|
-
true
|
|
58
|
-
else
|
|
59
|
-
false
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
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
|
|
63
49
|
end
|
|
64
50
|
|
|
65
51
|
|
|
66
|
-
|
|
67
52
|
def upcoming_matches( date: Date.today,
|
|
68
53
|
limit: nil )
|
|
69
54
|
## note: includes todays matches for now
|
|
70
|
-
matches =
|
|
55
|
+
matches = _select_matches { |match| date <= match['date'] }
|
|
71
56
|
|
|
72
57
|
if limit
|
|
73
58
|
matches[0, limit] ## cut-off
|
|
@@ -76,16 +61,34 @@ module Footty
|
|
|
76
61
|
end
|
|
77
62
|
end
|
|
78
63
|
|
|
64
|
+
|
|
79
65
|
def past_matches( date: Date.today )
|
|
80
|
-
matches =
|
|
66
|
+
matches = _select_matches { |match| date > match['date'] }
|
|
81
67
|
## note reveserve matches (chronological order/last first)
|
|
82
68
|
## matches.reverse
|
|
83
69
|
matches
|
|
84
70
|
end
|
|
85
71
|
|
|
86
72
|
|
|
87
|
-
|
|
88
|
-
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 )
|
|
89
92
|
selected = []
|
|
90
93
|
matches.each do |match|
|
|
91
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',
|
|
@@ -39,18 +41,24 @@ module Footty
|
|
|
39
41
|
'nl'=> 'europe/netherlands/$season$_nl1.txt',
|
|
40
42
|
'be'=> 'europe/belgium/$season$_be1.txt',
|
|
41
43
|
|
|
42
|
-
'champs'=> 'champions-league/$season$/cl.txt',
|
|
44
|
+
'champs' => 'champions-league/$season$/cl.txt',
|
|
45
|
+
'uefacl' => 'champions-league/$season$/cl.txt',
|
|
46
|
+
'uefael' => 'champions-league/$season$/el.txt',
|
|
47
|
+
'uefaconf' => 'champions-league/$season$/conf.txt',
|
|
48
|
+
|
|
43
49
|
|
|
44
50
|
'br' => 'south-america/brazil/$year$_br1.txt',
|
|
45
51
|
'ar' => 'south-america/argentina/$year$_ar1.txt',
|
|
46
52
|
'co' => 'south-america/colombia/$year$_co1.txt',
|
|
47
|
-
|
|
53
|
+
|
|
48
54
|
## use a different code for copa libertadores? why? why not?
|
|
49
55
|
'copa' => 'south-america/copa-libertadores/$year$_copal.txt',
|
|
50
56
|
|
|
51
57
|
'mx' => 'world/north-america/mexico/$season$_mx1.txt',
|
|
52
58
|
'mls' => 'world/north-america/major-league-soccer/$year$_mls.txt',
|
|
53
59
|
|
|
60
|
+
'concacafcl' => 'world/north-america/champions-league/$year$_concacafcl.txt',
|
|
61
|
+
|
|
54
62
|
'eg' => 'world/africa/egypt/$season$_eg1.txt',
|
|
55
63
|
'ma' => 'world/africa/morocco/$season$_ma1.txt',
|
|
56
64
|
|
|
@@ -74,10 +82,10 @@ module Footty
|
|
|
74
82
|
|
|
75
83
|
## todo/fix - report error if no spec found
|
|
76
84
|
season = if spec.is_a?( Hash ) ## assume lookup by year
|
|
77
|
-
spec.keys[0]
|
|
85
|
+
spec.keys[0]
|
|
78
86
|
else ## assume vanilla urls (no lookup by year)
|
|
79
|
-
## default to
|
|
80
|
-
spec.index( '$year$') ? '
|
|
87
|
+
## default to 2026 or 2025/26 for now
|
|
88
|
+
spec.index( '$year$') ? '2026' : '2025/26'
|
|
81
89
|
end
|
|
82
90
|
season
|
|
83
91
|
end
|
|
@@ -110,13 +118,27 @@ module Footty
|
|
|
110
118
|
end
|
|
111
119
|
|
|
112
120
|
matches = matches.map {|match| match.as_json } # convert to json
|
|
113
|
-
|
|
114
|
-
## note - sort by date/time
|
|
121
|
+
|
|
122
|
+
## note - sort by date/time
|
|
115
123
|
## (assume stable sort; no reshuffle of matches if already sorted by date/time)
|
|
116
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
|
+
|
|
117
139
|
matches = matches.sort do |l,r|
|
|
118
140
|
result = l['date'] <=> r['date']
|
|
119
|
-
result = l['time'] <=> r['time'] if result == 0 &&
|
|
141
|
+
result = l['time'] <=> r['time'] if result == 0 &&
|
|
120
142
|
(l['time'] && r['time'])
|
|
121
143
|
result
|
|
122
144
|
end
|
|
@@ -125,12 +147,13 @@ module Footty
|
|
|
125
147
|
end
|
|
126
148
|
|
|
127
149
|
|
|
150
|
+
|
|
128
151
|
def openfootball_url( path, season: )
|
|
129
152
|
repo, local_path = path.split( '/', 2)
|
|
130
153
|
url = "https://raw.githubusercontent.com/openfootball/#{repo}/master/#{local_path}"
|
|
131
154
|
## check for template vars too
|
|
132
155
|
season = Season( season )
|
|
133
|
-
url = url.gsub( '$year$',
|
|
156
|
+
url = url.gsub( '$year$', season.start_year.to_s )
|
|
134
157
|
url = url.gsub( '$season$', season.to_path )
|
|
135
158
|
url
|
|
136
159
|
end
|
|
@@ -138,7 +161,7 @@ module Footty
|
|
|
138
161
|
|
|
139
162
|
def matches() @matches; end
|
|
140
163
|
def league_name() @league_name; end
|
|
141
|
-
|
|
164
|
+
|
|
142
165
|
|
|
143
166
|
|
|
144
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,247 +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
9
|
|
|
14
|
-
require_relative 'footty/
|
|
10
|
+
require_relative 'footty/dataset/dataset'
|
|
11
|
+
require_relative 'footty/dataset/openfootball'
|
|
12
|
+
|
|
13
|
+
require_relative 'footty/pp_matches'
|
|
14
|
+
require_relative 'footty/pp_week'
|
|
15
|
+
|
|
16
|
+
require_relative 'footty/main'
|
|
17
|
+
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
## set cache to local .cache dir for now - why? why not?
|
|
19
23
|
Webcache.root = './cache'
|
|
24
|
+
|
|
20
25
|
# pp Webcache.root
|
|
21
26
|
Webget.config.sleep = 1 ## set delay in secs (to 1 sec - default is/maybe 3)
|
|
22
27
|
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
opts = { debug: false,
|
|
30
|
-
verbose: false, ## add more details
|
|
31
|
-
## add cache/cache_dir - why? why not?
|
|
32
|
-
|
|
33
|
-
query: nil,
|
|
34
|
-
|
|
35
|
-
## display format/mode - week/window/upcoming/past (default is today)
|
|
36
|
-
yesterday: nil,
|
|
37
|
-
tomorrow: nil,
|
|
38
|
-
upcoming: nil,
|
|
39
|
-
past: nil,
|
|
40
|
-
|
|
41
|
-
# week: nil,
|
|
42
|
-
# window: nil, ## 2 day plus/minus +2/-2
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
parser = OptionParser.new do |parser|
|
|
47
|
-
parser.banner = "Usage: #{$PROGRAM_NAME} [options] LEAGUES"
|
|
48
|
-
|
|
49
|
-
parser.on( "--verbose",
|
|
50
|
-
"turn on verbose output (default: #{opts[:verbose]})" ) do |verbose|
|
|
51
|
-
opts[:verbose] = true
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
parser.on( "-q NAME", "--query",
|
|
55
|
-
"query mode; display matches where team name matches query" ) do |query|
|
|
56
|
-
opts[:query] = query
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
parser.on( "-y", "--yesterday" ) do |yesterday|
|
|
61
|
-
opts[:yesterday] = true
|
|
62
|
-
end
|
|
63
|
-
parser.on( "-t", "--tomorrow" ) do |tomorrow|
|
|
64
|
-
opts[:tomorrow] = true
|
|
65
|
-
end
|
|
66
|
-
parser.on( "-p", "--past" ) do |past|
|
|
67
|
-
opts[:past] = true
|
|
68
|
-
end
|
|
69
|
-
parser.on( "-u", "--up", "--upcoming" ) do |upcoming|
|
|
70
|
-
opts[:upcoming] = true
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
parser.parse!( args )
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
puts "OPTS:"
|
|
77
|
-
p opts
|
|
78
|
-
puts "ARGV:"
|
|
79
|
-
p args
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
###
|
|
83
|
-
## use simple norm(alize) args (that is,) league codes for now
|
|
84
|
-
## - downcase, strip dot (.) etc.)
|
|
85
|
-
## e.g. en.facup => enfacup
|
|
86
|
-
## at.cup => atcup etc.
|
|
87
|
-
args = args.map { |arg| arg.downcase.gsub( /[._-]/, '' ) }
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
######################
|
|
92
|
-
## note - first check for buil-in "magic" commands
|
|
93
|
-
## e.g. leagues / codes - dump built-in league codes
|
|
94
|
-
|
|
95
|
-
if args.include?( 'leagues' )
|
|
96
|
-
puts "==> openfootball dataset sources:"
|
|
97
|
-
pp OpenfootballDataset::SOURCES
|
|
98
|
-
|
|
99
|
-
## pretty print keys/codes only
|
|
100
|
-
puts
|
|
101
|
-
puts OpenfootballDataset::SOURCES.keys.join( ' ' )
|
|
102
|
-
puts " #{OpenfootballDataset::SOURCES.keys.size} league code(s)"
|
|
103
|
-
|
|
104
|
-
exit 1
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
top = [['world', '2022'],
|
|
112
|
-
['euro', '2024'],
|
|
113
|
-
['mx', '2024/25'],
|
|
114
|
-
['copa', '2025'], ## copa libertadores
|
|
115
|
-
['en', '2024/25'],
|
|
116
|
-
['es', '2024/25'],
|
|
117
|
-
['it', '2024/25'],
|
|
118
|
-
['fr', '2024/25'],
|
|
119
|
-
['de', '2024/25'],
|
|
120
|
-
['at', '2024/25'],
|
|
121
|
-
['champs', '2024/25'],
|
|
122
|
-
]
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
leagues = if args.size == 0
|
|
126
|
-
top
|
|
127
|
-
else
|
|
128
|
-
### auto-fill (latest) season/year
|
|
129
|
-
args.map do |arg|
|
|
130
|
-
[arg, OpenfootballDataset.latest_season( league: arg )]
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
## fetch leagues
|
|
136
|
-
datasets = leagues.map do |league, season|
|
|
137
|
-
dataset = OpenfootballDataset.new( league: league, season: season )
|
|
138
|
-
## parse matches
|
|
139
|
-
matches = dataset.matches
|
|
140
|
-
puts " #{league} #{season} - #{matches.size} match(es)"
|
|
141
|
-
dataset
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
###################
|
|
147
|
-
## check for query option to filter matches by query (team)
|
|
148
|
-
if opts[:query]
|
|
149
|
-
q = opts[:query]
|
|
150
|
-
puts
|
|
151
|
-
puts
|
|
152
|
-
datasets.each do |dataset|
|
|
153
|
-
matches = dataset.query( q )
|
|
154
|
-
|
|
155
|
-
if matches.size == 0
|
|
156
|
-
## siltently skip for now
|
|
157
|
-
else ## assume matches found
|
|
158
|
-
print "==> #{dataset.league_name}"
|
|
159
|
-
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
160
|
-
print " -- #{dataset.matches.size} match(es)"
|
|
161
|
-
print "\n"
|
|
162
|
-
print_matches( matches )
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
exit 1
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# Dataset.new( league: 'euro', year: 2024 )
|
|
170
|
-
# dataset = Dataset.new( league: league, year: year )
|
|
171
|
-
|
|
172
|
-
## in the future make today "configurable" as param - why? why not?
|
|
173
|
-
today = Date.today
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
what = if opts[:yesterday]
|
|
177
|
-
'yesterday'
|
|
178
|
-
elsif opts[:tomorrow]
|
|
179
|
-
'tomorrow'
|
|
180
|
-
elsif opts[:past]
|
|
181
|
-
'past'
|
|
182
|
-
elsif opts[:upcoming]
|
|
183
|
-
'upcoming'
|
|
184
|
-
else
|
|
185
|
-
'today'
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
## start with two empty lines - assume (massive) debug output before ;-)
|
|
190
|
-
puts
|
|
191
|
-
puts
|
|
192
|
-
datasets.each do |dataset|
|
|
193
|
-
print "==> #{dataset.league_name}"
|
|
194
|
-
print " #{dataset.start_date} - #{dataset.end_date}"
|
|
195
|
-
print " -- #{dataset.matches.size} match(es)"
|
|
196
|
-
print "\n"
|
|
197
|
-
|
|
198
|
-
if what == 'yesterday'
|
|
199
|
-
matches = dataset.yesterdays_matches
|
|
200
|
-
if matches.empty?
|
|
201
|
-
puts (' '*4) + "** No matches played yesterday.\n"
|
|
202
|
-
end
|
|
203
|
-
elsif what == 'tomorrow'
|
|
204
|
-
matches = dataset.tomorrows_matches
|
|
205
|
-
if matches.empty?
|
|
206
|
-
puts (' '*4) + "** No matches scheduled tomorrow.\n"
|
|
207
|
-
end
|
|
208
|
-
elsif what == 'past'
|
|
209
|
-
matches = dataset.past_matches
|
|
210
|
-
if matches.empty?
|
|
211
|
-
puts (' '*4) + "** No matches played yet.\n"
|
|
212
|
-
end
|
|
213
|
-
elsif what == 'upcoming'
|
|
214
|
-
matches = dataset.upcoming_matches
|
|
215
|
-
if matches.empty?
|
|
216
|
-
puts (' '*4) + "** No more matches scheduled.\n"
|
|
217
|
-
end
|
|
218
|
-
else ## assume today
|
|
219
|
-
matches = dataset.todays_matches
|
|
220
|
-
|
|
221
|
-
## no matches today
|
|
222
|
-
if matches.empty?
|
|
223
|
-
puts (' '*4) + "** No matches scheduled today.\n"
|
|
224
|
-
|
|
225
|
-
if opts[:verbose]
|
|
226
|
-
## note: was world cup 2018 - end date -- Date.new( 2018, 7, 11 )
|
|
227
|
-
## note: was euro 2020 (in 2021) - end date -- Date.new( 2021, 7, 11 )
|
|
228
|
-
if Date.today > dataset.end_date ## tournament is over, look back
|
|
229
|
-
puts "Past matches:"
|
|
230
|
-
matches = dataset.past_matches
|
|
231
|
-
else ## world cup is upcoming /in-progress,look forward
|
|
232
|
-
puts "Upcoming matches:"
|
|
233
|
-
matches = dataset.upcoming_matches( limit: 18 )
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
print_matches( matches )
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
end # method self.main
|
|
242
|
-
end # module Footty
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
Footty.main if __FILE__ == $0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
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
|