footty 2024.5.10 → 2024.8.28

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34ac44dbe56d51fad5a516b69331ab594243cbac79f5c4b63f69271455773cd5
4
- data.tar.gz: 428605f76f7713f3e61f8cf53cf80d5c8c3d6add43f649a0d701e9ca465d4259
3
+ metadata.gz: 2866ba25c1b7a6d08787aaff2bff8821bbc92dada92bcc85ad084b65ceace8b9
4
+ data.tar.gz: bad3de36a89de81e96565aa3c5c245d5cc53b56ea2faddfae38a0e8810f565e2
5
5
  SHA512:
6
- metadata.gz: 359ab0cc33ca8b8482a1d2e9cb60ea58e96c95c5494dc35232018dba09fe164f22a8a167afc1536e90ed9dabb600fe412fab6d0c7aa1a4afa3e3967e3c857907
7
- data.tar.gz: 344366274d54e0721d8a718af983dd555009db88a8d11f89e56149bef17cb24ff23e17c772ca133a31bef0e0ac65a1dc68207b45e3c9a4c9c429241d30398599
6
+ metadata.gz: fabcae4fbe59527bfb4dc35555d0ffa87271240a8ee385a6ea26842d3d268d58b4d6109ebee9159c362b1327447e5d36fae63b2e51f07931d32e5578103d7a70
7
+ data.tar.gz: df46117a0ab611d90315a17525308f71bd82aac6b00c2a07388e115d92381efdf2f8d339e98311278a80b8b68e3f83283b2eaef3d9ee7ab2c01f753a252d90c5
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 2024.5.10
1
+ ### 2024.8.28
2
2
  ### 1.0.0 / 2018-06-09
3
3
 
4
4
  * Everything is new (again). First release.
data/Manifest.txt CHANGED
@@ -3,6 +3,7 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  bin/footty
6
+ bin/ftty
6
7
  lib/footty.rb
7
- lib/footty/client.rb
8
+ lib/footty/dataset.rb
8
9
  lib/footty/version.rb
data/Rakefile CHANGED
@@ -11,20 +11,19 @@ Hoe.spec 'footty' do
11
11
  self.urls = { home: 'https://github.com/sportdb/footty' }
12
12
 
13
13
  self.author = 'Gerald Bauer'
14
- self.email = 'opensport@googlegroups.com'
14
+ self.email = 'gerald.bauer@gmail.com'
15
15
 
16
16
  # switch extension to .markdown for gihub formatting
17
17
  self.readme_file = 'README.md'
18
18
  self.history_file = 'CHANGELOG.md'
19
19
 
20
20
  self.extra_deps = [
21
- ['logutils' ],
22
- ['fetcher']
21
+ ['sportdb-quick']
23
22
  ]
24
23
 
25
24
  self.licenses = ['Public Domain']
26
25
 
27
26
  self.spec_extras = {
28
- required_ruby_version: '>= 2.3'
27
+ required_ruby_version: '>= 3.1.0'
29
28
  }
30
29
  end
data/bin/ftty ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## tip: to test run:
4
+ ## ruby -I ./lib bin/ftty
5
+
6
+ require 'footty'
7
+
8
+ Footty.main
@@ -0,0 +1,142 @@
1
+ module Footty
2
+
3
+
4
+
5
+ class Dataset
6
+
7
+ def initialize( league:, year: )
8
+ @league = league
9
+ @year = year
10
+ @season = Season( "#{year}/#{year+1}" )
11
+ end
12
+
13
+
14
+ APIS = {
15
+ world: 'https://raw.githubusercontent.com/openfootball/worldcup.json/master/$year$/worldcup.json',
16
+ euro: 'https://raw.githubusercontent.com/openfootball/euro.json/master/$year$/euro.json',
17
+ de: 'https://raw.githubusercontent.com/openfootball/deutschland/master/$season$/1-bundesliga.txt',
18
+ en: 'https://raw.githubusercontent.com/openfootball/england/master/$season$/1-premierleague.txt',
19
+ at: 'https://raw.githubusercontent.com/openfootball/austria/master/$season$/1-bundesliga-i.txt',
20
+ }
21
+
22
+ ### note:
23
+ ## cache ALL methods - only do one web request for match schedule & results
24
+ def matches
25
+ @data ||= begin
26
+ url = APIS[ @league.downcase.to_sym ]
27
+ url = url.gsub( '$year$', @year.to_s )
28
+ url = url.gsub( '$season$', @season.to_path )
29
+
30
+ res = get!( url ) ## use "memoized" / cached result
31
+ if url.end_with?( '.json' )
32
+ JSON.parse( res.text )
33
+ else ## assume football.txt format
34
+ matches = SportDb::QuickMatchReader.parse( res.text )
35
+ data = matches.map {|match| match.as_json } # convert to json
36
+ ## quick hack to get keys as strings not symbols!!
37
+ ## fix upstream
38
+ JSON.parse( JSON.generate( data ))
39
+ end
40
+ end
41
+ end
42
+
43
+ def end_date
44
+ @end_date ||= begin
45
+ end_date = nil
46
+ each do |match|
47
+ date = Date.strptime(match['date'], '%Y-%m-%d' )
48
+ end_date = date if end_date.nil? ||
49
+ date > end_date
50
+ end
51
+ end_date
52
+ end
53
+ end
54
+
55
+ def start_date
56
+ @start_date ||= begin
57
+ start_date = nil
58
+ each do |match|
59
+ date = Date.strptime(match['date'], '%Y-%m-%d' )
60
+ start_date = date if start_date.nil? ||
61
+ date < start_date
62
+ end
63
+ start_date
64
+ end
65
+ end
66
+
67
+ def todays_matches( date: Date.today ) matches_for( date ); end
68
+ def tomorrows_matches( date: Date.today ) matches_for( date+1 ); end
69
+ def yesterdays_matches( date: Date.today ) matches_for( date-1 ); end
70
+
71
+ def matches_for( date )
72
+ matches = select_matches { |match| date == Date.parse( match['date'] ) }
73
+ matches
74
+ end
75
+
76
+
77
+ def upcoming_matches( date: Date.today,
78
+ limit: nil )
79
+ ## note: includes todays matches for now
80
+ matches = select_matches { |match| date <= Date.parse( match['date'] ) }
81
+
82
+ if limit
83
+ matches[0, limit] ## cut-off
84
+ else
85
+ matches
86
+ end
87
+ end
88
+
89
+ def past_matches( date: Date.today )
90
+ matches = select_matches { |match| date > Date.parse( match['date'] ) }
91
+ ## note reveserve matches (chronological order/last first)
92
+ matches.reverse
93
+ end
94
+
95
+
96
+ private
97
+ def each
98
+ data = matches
99
+ if data.is_a?( Hash) && data.has_key?('rounds')
100
+ data['rounds'].each do |round|
101
+ round['matches'].each do |match|
102
+ ## hack: add (outer) round to match
103
+ match['round'] = round['name']
104
+ yield( match )
105
+ end
106
+ end
107
+ else ## assume flat matches in Array
108
+ data.each do |match|
109
+ yield( match )
110
+ end
111
+ end
112
+ end
113
+
114
+
115
+ def select_matches
116
+ matches = []
117
+ each do |match|
118
+ matches << match if yield( match )
119
+ end
120
+
121
+ ## todo/fix:
122
+ ## sort matches here; might not be chronologicial (by default)
123
+ matches
124
+ end # method select_matches
125
+
126
+
127
+ def get!( url )
128
+ response = Webclient.get( url )
129
+
130
+ if response.status.ok?
131
+ response
132
+ else
133
+ puts "!! HTTP ERROR - #{response.status.code} #{response.status.message}"
134
+ ## dump headers
135
+ response.headers.each do |key,value|
136
+ puts " #{key}: #{value}"
137
+ end
138
+ exit 1
139
+ end
140
+ end # method get!
141
+ end # class Client
142
+ end # module Footty
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Footty
3
- VERSION = '2024.5.10'
3
+ VERSION = '2024.8.28'
4
4
 
5
5
  def self.banner
6
6
  "footty/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
data/lib/footty.rb CHANGED
@@ -1,63 +1,70 @@
1
- ## stdlibs
2
-
3
- require 'net/http'
4
- require 'uri'
5
- require 'json'
6
- require 'pp'
7
-
8
-
9
- ## 3rd party gems/libs
10
- ## require 'props'
11
-
12
- require 'logutils'
13
- require 'fetcher'
1
+ require 'sportdb/quick' ## note - pulls in cocos et al
14
2
 
15
3
 
16
4
  # our own code
17
- require 'footty/version' # let it always go first
18
- require 'footty/client'
5
+ require_relative 'footty/version' # let it always go first
6
+ require_relative 'footty/dataset'
19
7
 
20
8
 
21
9
  module Footty
22
10
 
23
- def self.client
24
- ## note: hard code tournament / league for now
25
- @client ||= Client.new( league: 'euro', year: 2024 ) ## use "singelton" / shared client
26
- end
27
-
28
-
29
-
30
11
  def self.main
31
12
  puts banner # say hello
32
13
 
33
- what = ARGV[0] || 'today'
14
+ league = 'en'
15
+ year = 2024
16
+
17
+ ## filter ARGV for league or year
18
+ args = ARGV.select do |arg|
19
+ if arg =~ /^\d{4}$/
20
+ year = arg.to_i(10)
21
+ false ## eat-up
22
+ elsif ['world',
23
+ 'euro',
24
+ 'de',
25
+ 'en',
26
+ 'at'].include?( arg )
27
+ league = arg
28
+ false
29
+ else
30
+ true
31
+ end
32
+ end
33
+
34
+
35
+ what = args[0] || 'today'
34
36
  what = what.downcase
35
37
 
38
+
39
+ # Dataset.new( league: 'euro', year: 2024 )
40
+ dataset = Dataset.new( league: league, year: year )
41
+
42
+
36
43
  ## in the future make today "configurable" as param - why? why not?
37
44
  today = Date.today
38
45
 
39
46
  if ['yesterday', 'y', '-1'].include?( what )
40
- matches = client.yesterdays_matches
47
+ matches = dataset.yesterdays_matches
41
48
  if matches.empty?
42
49
  puts "** No matches played yesterday.\n"
43
50
  end
44
51
  elsif ['tomorrow', 't', '+1', '1'].include?( what )
45
- matches = client.tomorrows_matches
52
+ matches = dataset.tomorrows_matches
46
53
  if matches.empty?
47
54
  puts "** No matches scheduled tomorrow.\n"
48
55
  end
49
56
  elsif ['past', 'p', 'prev'].include?( what )
50
- matches = client.past_matches
57
+ matches = dataset.past_matches
51
58
  if matches.empty?
52
59
  puts "** No matches played yet.\n"
53
60
  end
54
61
  elsif ['upcoming', 'up', 'u', 'next', 'n'].include?( what )
55
- matches = client.upcoming_matches
62
+ matches = dataset.upcoming_matches
56
63
  if matches.empty?
57
64
  puts "** No more matches scheduled.\n"
58
65
  end
59
66
  else
60
- matches = client.todays_matches
67
+ matches = dataset.todays_matches
61
68
 
62
69
  ## no matches today
63
70
  if matches.empty?
@@ -65,50 +72,71 @@ module Footty
65
72
 
66
73
  ## note: was world cup 2018 - end date -- Date.new( 2018, 7, 11 )
67
74
  ## note: was euro 2020 (in 2021) - end date -- Date.new( 2021, 7, 11 )
68
- if Date.today > Date.new( 2024, 7, 14 ) ## tournament is over, look back
75
+ if Date.today > dataset.end_date ## tournament is over, look back
69
76
  puts "Past matches:"
70
- matches = client.past_matches
77
+ matches = dataset.past_matches
71
78
  else ## world cup is upcoming /in-progress,look forward
72
79
  puts "Upcoming matches:"
73
- matches = client.upcoming_matches
80
+ matches = dataset.upcoming_matches( limit: 18 )
74
81
  end
75
82
  end
76
83
  end
77
84
 
78
-
79
-
80
85
  print_matches( matches )
81
86
  end
82
87
 
83
88
 
84
-
85
89
  def self.print_matches( matches )
86
90
  ## print games
87
91
 
88
92
  today = Date.today
89
93
 
90
94
  matches.each do |match|
91
- print " %5s" % "\##{match['num']} "
95
+ print " %5s" % "\##{match['num']} " if match['num']
92
96
 
93
- date = Date.parse( match['date'] )
97
+ date = Date.strptime( match['date'], '%Y-%m-%d' )
94
98
  print "#{date.strftime('%a %b/%d')} " ## e.g. Thu Jun/14
99
+ print "#{match['time']} " if match['time']
100
+
95
101
  if date > today
96
102
  diff = (date - today).to_i
97
103
  print "%10s" % "(in #{diff}d) "
98
104
  end
99
105
 
100
- print "%22s" % "#{match['team1']['name']} (#{match['team1']['code']})"
101
106
 
102
- ## todo/fix: add support for knockout scores
103
- ## with score1et/score1p (extra time and penalty)
104
- if match['score1'] && match['score2']
107
+ if match['team1'].is_a?( Hash )
108
+ print "%22s" % "#{match['team1']['name']} (#{match['team1']['code']})"
109
+ else
110
+ print "%22s" % "#{match['team1']}"
111
+ end
112
+
113
+
114
+ if match['score'].is_a?( Hash ) &&
115
+ match['score']['ft']
116
+ if match['score']['ft']
117
+ print " #{match['score']['ft'][0]}-#{match['score']['ft'][1]} "
118
+ end
119
+ if match['score']['et']
120
+ print "aet #{match['score']['et'][0]}-#{match['score']['et'][1]} "
121
+ end
122
+ if match['score']['p']
123
+ print "pen #{match['score']['p'][0]}-#{match['score']['p'][1]} "
124
+ end
125
+ elsif match['score1'] && match['score2']
126
+ ## todo/fix: add support for knockout scores
127
+ ## with score1et/score1p (extra time and penalty)
105
128
  print " #{match['score1']}-#{match['score2']} "
106
129
  print "(#{match['score1i']}-#{match['score2i']}) "
107
130
  else
108
131
  print " vs "
109
132
  end
110
133
 
111
- print "%-22s" % "#{match['team2']['name']} (#{match['team2']['code']})"
134
+ if match['team2'].is_a?( Hash )
135
+ print "%-22s" % "#{match['team2']['name']} (#{match['team2']['code']})"
136
+ else
137
+ print "%-22s" % "#{match['team2']}"
138
+ end
139
+
112
140
 
113
141
  if match['group']
114
142
  print " #{match['group']} /" ## group phase/stage
@@ -149,8 +177,6 @@ module Footty
149
177
  end
150
178
  print "]\n"
151
179
  end
152
-
153
-
154
180
  end
155
181
  end
156
182
 
metadata CHANGED
@@ -1,31 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: footty
3
3
  version: !ruby/object:Gem::Version
4
- version: 2024.5.10
4
+ version: 2024.8.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-10 00:00:00.000000000 Z
11
+ date: 2024-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: logutils
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: fetcher
14
+ name: sportdb-quick
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - ">="
@@ -74,9 +60,10 @@ dependencies:
74
60
  version: '4.1'
75
61
  description: footty - football.db command line client for european "euro" championship
76
62
  2024 and more - who is playing today?
77
- email: opensport@googlegroups.com
63
+ email: gerald.bauer@gmail.com
78
64
  executables:
79
65
  - footty
66
+ - ftty
80
67
  extensions: []
81
68
  extra_rdoc_files:
82
69
  - CHANGELOG.md
@@ -88,8 +75,9 @@ files:
88
75
  - README.md
89
76
  - Rakefile
90
77
  - bin/footty
78
+ - bin/ftty
91
79
  - lib/footty.rb
92
- - lib/footty/client.rb
80
+ - lib/footty/dataset.rb
93
81
  - lib/footty/version.rb
94
82
  homepage: https://github.com/sportdb/footty
95
83
  licenses:
@@ -105,7 +93,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
93
  requirements:
106
94
  - - ">="
107
95
  - !ruby/object:Gem::Version
108
- version: '2.3'
96
+ version: 3.1.0
109
97
  required_rubygems_version: !ruby/object:Gem::Requirement
110
98
  requirements:
111
99
  - - ">="
data/lib/footty/client.rb DELETED
@@ -1,106 +0,0 @@
1
- module Footty
2
-
3
-
4
- class Client
5
-
6
- include LogUtils::Logging
7
-
8
-
9
- def initialize( league:, year: )
10
- @worker = Fetcher::Worker.new
11
-
12
- @league = league
13
- @year = year
14
- end
15
-
16
-
17
- APIS = {
18
- worldcup: 'https://github.com/openfootball/worldcup.json/raw/master/$year$/worldcup.json',
19
- euro: 'https://github.com/openfootball/euro.json/raw/master/$year$/euro.json'
20
- }
21
-
22
- ### note:
23
- ## cache ALL methods - only do one web request for match schedule & results
24
- def get_matches
25
- @data ||= begin
26
- str = APIS[ @league.downcase.to_sym ]
27
- str = str.gsub( '$year$', @year.to_s )
28
-
29
- get( str ) ## use "memoized" / cached result
30
- end
31
- end
32
-
33
-
34
-
35
-
36
- ## for testing lets you use /round/1 etc.
37
- def round( num )
38
- h = get_matches
39
- matches = h[ 'rounds' ][ num-1 ] ## note: rounds hash starts with zero (not 1)
40
- matches
41
- end
42
-
43
-
44
- def todays_matches( date: Date.today ) matches_for( date ); end
45
- def tomorrows_matches( date: Date.today ) matches_for( date+1 ); end
46
- def yesterdays_matches( date: Date.today ) matches_for( date-1 ); end
47
-
48
- def matches_for( date )
49
- hash = get_matches
50
- matches = select_matches( hash[ 'rounds' ] ) { |match| date == Date.parse( match['date'] ) }
51
- matches
52
- end
53
-
54
-
55
- def upcoming_matches( date: Date.today )
56
- ## note: includes todays matches for now
57
- hash = get_matches
58
- matches = select_matches( hash[ 'rounds' ] ) { |match| date <= Date.parse( match['date'] ) }
59
- matches
60
- end
61
-
62
- def past_matches( date: Date.today )
63
- hash = get_matches
64
- matches = select_matches( hash[ 'rounds' ] ) { |match| date > Date.parse( match['date'] ) }
65
- ## note reveserve matches (chronological order/last first)
66
- matches.reverse
67
- end
68
-
69
- private
70
-
71
- def select_matches( rounds )
72
- matches = []
73
- rounds.each do |round|
74
- round['matches'].each do |match|
75
- if yield( match )
76
- ## hack: add (outer) round to match
77
- match['round'] = round['name']
78
- matches << match
79
- else
80
- ## puts " skipping game play_date #{play_date}"
81
- end
82
- end
83
- end
84
- matches
85
- end
86
-
87
-
88
- def get( str )
89
- response = @worker.get_response( str )
90
-
91
- if response.code == '200'
92
- ##
93
- ## fix/fix/todo/check:
94
- ## do we need to force utf-8 encoding? yes!!!!
95
- ## check for teams w/ non-ascii names
96
- hash = JSON.parse( response.body )
97
- ## pp hash
98
- hash
99
- else
100
- logger.error "fetch HTTP - #{response.code} #{response.message}"
101
- nil
102
- end
103
- end
104
-
105
- end # class Client
106
- end # module Footty