foot_stats 0.0.1 → 0.1.0
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.
- 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
|