footty 2024.5.10 → 2024.8.28

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 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