foot_stats 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -5
- data/foot_stats.gemspec +5 -2
- data/lib/foot_stats.rb +20 -9
- data/lib/foot_stats/attribute_accessor.rb +24 -0
- data/lib/foot_stats/championship.rb +13 -10
- data/lib/foot_stats/championship_classification.rb +7 -2
- data/lib/foot_stats/live.rb +120 -0
- data/lib/foot_stats/live_card.rb +14 -0
- data/lib/foot_stats/live_goal.rb +14 -0
- data/lib/foot_stats/live_player.rb +28 -0
- data/lib/foot_stats/live_team.rb +55 -0
- data/lib/foot_stats/match.rb +42 -9
- data/lib/foot_stats/narration.rb +35 -33
- data/lib/foot_stats/player.rb +55 -0
- data/lib/foot_stats/redis_payload_store.rb +40 -0
- data/lib/foot_stats/request.rb +9 -6
- data/lib/foot_stats/resource.rb +27 -0
- data/lib/foot_stats/response.rb +57 -8
- data/lib/foot_stats/round_match.rb +23 -0
- data/lib/foot_stats/setup.rb +14 -11
- data/lib/foot_stats/stream.rb +32 -0
- data/lib/foot_stats/team.rb +16 -3
- data/lib/foot_stats/version.rb +1 -1
- data/spec/cassettes/live_match.yml +240 -0
- data/spec/cassettes/match_narration.yml +13 -11
- data/spec/cassettes/team_player.yml +148 -0
- data/spec/foot_stats/live_spec.rb +59 -0
- data/spec/foot_stats/match_spec.rb +1 -1
- data/spec/foot_stats/narration_spec.rb +0 -7
- data/spec/foot_stats/player_spec.rb +34 -0
- data/spec/foot_stats/stream_spec.rb +61 -0
- metadata +79 -7
- data/spec/cassettes/match_narration_error_response.yml +0 -63
data/README.md
CHANGED
@@ -20,10 +20,11 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
FootStats::Setup.setup do |config|
|
23
|
-
config.username
|
24
|
-
config.password
|
25
|
-
config.logger
|
26
|
-
config.base_url
|
23
|
+
config.username = "username"
|
24
|
+
config.password = "password"
|
25
|
+
config.logger = Rails.logger
|
26
|
+
config.base_url = "http://footstats.com"
|
27
|
+
config.payload_store = Redis.new
|
27
28
|
end
|
28
29
|
```
|
29
30
|
|
@@ -60,7 +61,6 @@ Or install it yourself as:
|
|
60
61
|
|
61
62
|
## Next
|
62
63
|
|
63
|
-
* LIVE 'feed'.
|
64
64
|
* Create a Fake App (Sandbox), that simulates FootStats API.
|
65
65
|
|
66
66
|
## Contributing
|
data/foot_stats.gemspec
CHANGED
@@ -17,10 +17,13 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.add_dependency '
|
20
|
+
gem.add_dependency 'json'
|
21
|
+
gem.add_dependency 'redis'
|
22
|
+
gem.add_dependency 'redis-namespace'
|
21
23
|
gem.add_dependency 'rest-client'
|
22
24
|
|
23
|
-
gem.add_development_dependency 'rspec'
|
24
25
|
gem.add_development_dependency 'vcr'
|
26
|
+
gem.add_development_dependency 'rake'
|
27
|
+
gem.add_development_dependency 'rspec'
|
25
28
|
gem.add_development_dependency 'fakeweb'
|
26
29
|
end
|
data/lib/foot_stats.rb
CHANGED
@@ -1,14 +1,25 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'rest-client'
|
6
|
+
|
1
7
|
require 'foot_stats/version'
|
2
8
|
|
3
9
|
module FootStats
|
4
|
-
autoload :
|
10
|
+
autoload :AttributeAccessor, 'foot_stats/attribute_accessor'
|
11
|
+
autoload :Championship, 'foot_stats/championship'
|
5
12
|
autoload :ChampionshipClassification, 'foot_stats/championship_classification'
|
6
|
-
autoload :ErrorResponse,
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
13
|
+
autoload :ErrorResponse, 'foot_stats/error_response'
|
14
|
+
autoload :Live, 'foot_stats/live'
|
15
|
+
autoload :Match, 'foot_stats/match'
|
16
|
+
autoload :Narration, 'foot_stats/narration'
|
17
|
+
autoload :Player, 'foot_stats/player'
|
18
|
+
autoload :Request, 'foot_stats/request'
|
19
|
+
autoload :Resource, 'foot_stats/resource'
|
20
|
+
autoload :Response, 'foot_stats/response'
|
21
|
+
autoload :RoundMatch, 'foot_stats/round_match'
|
22
|
+
autoload :Setup, 'foot_stats/setup'
|
23
|
+
autoload :Stream, 'foot_stats/stream'
|
24
|
+
autoload :Team, 'foot_stats/team'
|
14
25
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FootStats
|
2
|
+
module AttributeAccessor
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def attributes
|
8
|
+
attrs = Hash.new
|
9
|
+
self.class.attributes.each{ |attribute| attrs[attribute] = send attribute }
|
10
|
+
attrs
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def attributes
|
15
|
+
@attributes ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
def attribute_accessor(*attrs)
|
19
|
+
self.attributes.concat attrs
|
20
|
+
attr_accessor *attrs
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,17 +1,20 @@
|
|
1
1
|
module FootStats
|
2
2
|
class Championship < Resource
|
3
|
-
|
3
|
+
attribute_accessor :source_id, :name, :has_classification, :current_round, :total_rounds
|
4
4
|
|
5
5
|
# Retrieve all championships from FootStats
|
6
6
|
#
|
7
7
|
# @return [Array]
|
8
8
|
#
|
9
|
-
def self.all
|
10
|
-
|
11
|
-
response = request.parse
|
9
|
+
def self.all(options = {})
|
10
|
+
response = Request.new(self).parse stream_key: 'championships'
|
12
11
|
|
13
12
|
return response.error if response.error?
|
14
13
|
|
14
|
+
updated_response response, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse_response(response)
|
15
18
|
response.collect do |championship|
|
16
19
|
new(
|
17
20
|
:source_id => championship['@Id'].to_i,
|
@@ -43,24 +46,24 @@ module FootStats
|
|
43
46
|
#
|
44
47
|
# @return [Array]
|
45
48
|
#
|
46
|
-
def classification
|
47
|
-
ChampionshipClassification.all(championship: source_id)
|
49
|
+
def classification(options = {})
|
50
|
+
ChampionshipClassification.all(options.merge(championship: source_id))
|
48
51
|
end
|
49
52
|
|
50
53
|
# Return the Championship teams.
|
51
54
|
#
|
52
55
|
# @return [Array]
|
53
56
|
#
|
54
|
-
def teams
|
55
|
-
Team.all(championship: source_id)
|
57
|
+
def teams(options = {})
|
58
|
+
Team.all(options.merge(championship: source_id))
|
56
59
|
end
|
57
60
|
|
58
61
|
# Return all the Championship matches.
|
59
62
|
#
|
60
63
|
# @return [Array]
|
61
64
|
#
|
62
|
-
def matches
|
63
|
-
Match.all(championship: source_id)
|
65
|
+
def matches(options = {})
|
66
|
+
Match.all(options.merge(championship: source_id))
|
64
67
|
end
|
65
68
|
end
|
66
69
|
end
|
@@ -6,11 +6,16 @@ module FootStats
|
|
6
6
|
attr_accessor :use
|
7
7
|
|
8
8
|
def self.all(options={})
|
9
|
-
|
10
|
-
|
9
|
+
championship_id = options.fetch(:championship)
|
10
|
+
request = Request.new self, :Campeonato => championship_id
|
11
|
+
response = request.parse stream_key: "championship-classification-#{championship_id}"
|
11
12
|
|
12
13
|
return response.error if response.error?
|
13
14
|
|
15
|
+
updated_response response, options
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parse_response(response)
|
14
19
|
response["Classificacoes"]['Classificacao']['Equipe'].collect do |classification|
|
15
20
|
ChampionshipClassification.new(
|
16
21
|
:team_source_id => classification['@Id'].to_i,
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module FootStats
|
2
|
+
class Live < Resource
|
3
|
+
autoload :Card, 'foot_stats/live_card'
|
4
|
+
autoload :Goal, 'foot_stats/live_goal'
|
5
|
+
autoload :Player, 'foot_stats/live_player'
|
6
|
+
autoload :Team, 'foot_stats/live_team'
|
7
|
+
|
8
|
+
MAPPED_PARAMS = {
|
9
|
+
source_id: '@IdPartida',
|
10
|
+
score: '@Placar',
|
11
|
+
penalty_score: '@PlacarPenaltis',
|
12
|
+
date: '@Data',
|
13
|
+
period: '@Periodo',
|
14
|
+
referee: '@Arbitro',
|
15
|
+
stadium: '@Estadio',
|
16
|
+
city: '@Cidade',
|
17
|
+
country: '@Pais',
|
18
|
+
has_narration: '@TemNarracao',
|
19
|
+
round: '@Rodada',
|
20
|
+
phase: '@Fase',
|
21
|
+
cup: '@Taca',
|
22
|
+
group: '@Grupo',
|
23
|
+
home_team: 'Mandante',
|
24
|
+
visitor_team: 'Visitante'
|
25
|
+
}
|
26
|
+
|
27
|
+
attr_accessor *MAPPED_PARAMS.keys
|
28
|
+
|
29
|
+
alias :narration? :has_narration
|
30
|
+
|
31
|
+
def home_score
|
32
|
+
score[:home]
|
33
|
+
end
|
34
|
+
|
35
|
+
def visitor_score
|
36
|
+
score[:visitor]
|
37
|
+
end
|
38
|
+
|
39
|
+
def home_penalty_score
|
40
|
+
penalty_score[:home]
|
41
|
+
end
|
42
|
+
|
43
|
+
def visitor_penalty_score
|
44
|
+
penalty_score[:visitor]
|
45
|
+
end
|
46
|
+
|
47
|
+
alias :narration? :has_narration
|
48
|
+
|
49
|
+
class << self
|
50
|
+
# Retrieve live data of a match
|
51
|
+
#
|
52
|
+
# @return [Live]
|
53
|
+
#
|
54
|
+
def find(match_id, options={})
|
55
|
+
response = Request.new(self, :IdPartida => match_id).parse stream_key: 'live_match'
|
56
|
+
return response.error if response.error?
|
57
|
+
|
58
|
+
updated_response response, options
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the resource name to request to FootStats.
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
#
|
65
|
+
def resource_name
|
66
|
+
'AoVivo'
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the resource key that is fetch from the API response.
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
#
|
73
|
+
def resource_key
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
def parse_response(response)
|
79
|
+
live_params = Hash.new
|
80
|
+
|
81
|
+
MAPPED_PARAMS.each do |key, foot_stats_key|
|
82
|
+
setter_method = :"parse_#{key}"
|
83
|
+
if respond_to? setter_method
|
84
|
+
live_params[key] = send setter_method, response[foot_stats_key]
|
85
|
+
else
|
86
|
+
live_params[key] = response[foot_stats_key]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
self.new live_params
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_int(value)
|
94
|
+
value.to_i
|
95
|
+
end
|
96
|
+
alias :parse_source_id :parse_int
|
97
|
+
alias :parse_round :parse_int
|
98
|
+
|
99
|
+
def parse_score(score)
|
100
|
+
home_score, visitor_score = score.split '-'
|
101
|
+
{
|
102
|
+
home: (home_score || 0).to_i,
|
103
|
+
visitor: (visitor_score || 0).to_i
|
104
|
+
}
|
105
|
+
end
|
106
|
+
alias :parse_penalty_score :parse_score
|
107
|
+
|
108
|
+
def parse_has_narration(narration)
|
109
|
+
narration == 'Sim'
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_team(team_params)
|
113
|
+
Team.new team_params
|
114
|
+
end
|
115
|
+
alias :parse_home_team :parse_team
|
116
|
+
alias :parse_visitor_team :parse_team
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module FootStats
|
2
|
+
class Live < Resource
|
3
|
+
class Card
|
4
|
+
attr_reader :player_name, :period, :minute, :type
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@player_name = params['@Jogador']
|
8
|
+
@period = params['@Periodo']
|
9
|
+
@minute = params['@Minuto'].to_i
|
10
|
+
@type = params['@Tipo']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module FootStats
|
2
|
+
class Live < Resource
|
3
|
+
class Goal
|
4
|
+
attr_reader :player_name, :period, :minute, :type
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@player_name = params['@Jogador']
|
8
|
+
@period = params['@Periodo']
|
9
|
+
@minute = params['@Minuto'].to_i
|
10
|
+
@type = params['@Tipo']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FootStats
|
2
|
+
class Live < Resource
|
3
|
+
class Player
|
4
|
+
attr_reader :source_id, :name, :status, :was_substituted, :substituted, :period, :minute
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@source_id = params["@IdJogador"].to_i
|
8
|
+
@name = params["@Jogador"]
|
9
|
+
@status = params["@Status"]
|
10
|
+
@was_substituted = params["@Substituto"] != ''
|
11
|
+
@period = params["@Periodo"]
|
12
|
+
@minute = params["@Minuto"].to_i
|
13
|
+
|
14
|
+
@substituted = params["@Substituto"] if @was_substituted
|
15
|
+
end
|
16
|
+
|
17
|
+
alias :substituted? :was_substituted
|
18
|
+
|
19
|
+
def self.diff(players, old_players_ids)
|
20
|
+
new_players_ids = players.map &:source_id
|
21
|
+
{
|
22
|
+
added: (new_players_ids - old_players_ids),
|
23
|
+
removed: (old_players_ids - new_players_ids)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module FootStats
|
2
|
+
class Live < Resource
|
3
|
+
class Team
|
4
|
+
attr_reader :coach, :full_name, :source_id
|
5
|
+
attr_reader :cards, :players, :goals
|
6
|
+
|
7
|
+
def initialize(params)
|
8
|
+
@coach = params['@Tecnico']
|
9
|
+
@full_name = params['@Nome']
|
10
|
+
@source_id = params['@Id']
|
11
|
+
|
12
|
+
parse_cards params['Cartoes']
|
13
|
+
parse_goals params['Gols']
|
14
|
+
parse_players params['Escalacao']
|
15
|
+
end
|
16
|
+
|
17
|
+
def initial_players
|
18
|
+
players.find_all{ |player| !player.substituted? }
|
19
|
+
end
|
20
|
+
|
21
|
+
def substituted_players
|
22
|
+
players.find_all{ |player| player.substituted? }
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def fix_collection(collection, key)
|
27
|
+
return [] if collection.nil?
|
28
|
+
|
29
|
+
if collection[key].is_a? Array
|
30
|
+
collection[key]
|
31
|
+
else
|
32
|
+
[ collection[key] ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_goals(goals)
|
37
|
+
@goals = fix_collection(goals, 'Gol').map do |goal_params|
|
38
|
+
Goal.new goal_params
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_cards(cards)
|
43
|
+
@cards = fix_collection(cards, 'Cartoes').map do |card_params|
|
44
|
+
Card.new card_params
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_players(players)
|
49
|
+
@players = fix_collection(players, 'Jogador').map do |player_params|
|
50
|
+
Player.new player_params
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/foot_stats/match.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
module FootStats
|
2
2
|
class Match < Resource
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
attribute_accessor :source_id, :date, :status, :referee, :stadium, :city, :state
|
4
|
+
attribute_accessor :country, :has_statistic, :has_narration, :round, :phase
|
5
|
+
attribute_accessor :cup, :group, :game_number, :live
|
6
|
+
attribute_accessor :home_team, :home_team_name, :home_score, :home_penalties_score
|
7
|
+
attribute_accessor :visitor_team, :visitor_team_name, :visitor_score, :visitor_penalties_score
|
8
8
|
|
9
9
|
def self.all(options={})
|
10
|
-
|
11
|
-
|
10
|
+
championship_id = options.fetch(:championship)
|
11
|
+
request = Request.new self, :Campeonato => options.fetch(:championship)
|
12
|
+
response = request.parse stream_key: "match_championship-#{championship_id}"
|
12
13
|
|
13
14
|
return response.error if response.error?
|
14
15
|
|
16
|
+
updated_response response, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_response(response)
|
15
20
|
response['Partida'].collect do |match|
|
16
21
|
match_object = Match.new(
|
17
22
|
:source_id => match['@Id'].to_i,
|
@@ -70,8 +75,36 @@ module FootStats
|
|
70
75
|
#
|
71
76
|
# @return [Array]
|
72
77
|
#
|
73
|
-
def narrations
|
74
|
-
Narration.all(match: source_id)
|
78
|
+
def narrations(options = {})
|
79
|
+
Narration.all(options.merge(match: source_id))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return live date of a match
|
83
|
+
#
|
84
|
+
# @return [Live]
|
85
|
+
#
|
86
|
+
def live(options = {})
|
87
|
+
Live.find source_id, options
|
88
|
+
end
|
89
|
+
|
90
|
+
# Live setter
|
91
|
+
#
|
92
|
+
# @return [boolean]
|
93
|
+
#
|
94
|
+
def live=(value)
|
95
|
+
if value == 'Sim' || value == true
|
96
|
+
@has_live = true
|
97
|
+
else
|
98
|
+
@has_live = false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Checks if match has live coverage
|
103
|
+
#
|
104
|
+
# @return [boolean]
|
105
|
+
#
|
106
|
+
def live?
|
107
|
+
@has_live
|
75
108
|
end
|
76
109
|
end
|
77
110
|
end
|