• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yurak / fanta / #639064463

26 Apr 2026 08:58PM UTC coverage: 0.0%. First build
#639064463

Pull #511

travis-ci

Pull Request #511: Add pg gem and configs, fix specs

0 of 1 new or added line in 1 file covered. (0.0%)

0 of 8337 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/models/player.rb
1
class Player < ApplicationRecord
×
2
  belongs_to :club
×
3
  belongs_to :national_team, optional: true
×
4

5
  has_many :player_positions, -> { order(:position_id) }, dependent: :destroy, inverse_of: :player
×
6
  has_many :positions, -> { order(:id) }, through: :player_positions
×
7

8
  has_many :player_teams, dependent: :destroy
×
9
  has_many :teams, through: :player_teams
×
10

11
  has_many :player_bids, dependent: :destroy
×
12
  has_many :player_requests, dependent: :destroy
×
13
  has_many :player_season_stats, dependent: :destroy
×
14
  has_many :round_players, dependent: :destroy
×
15
  has_many :transfers, dependent: :destroy
×
16
  has_many :club_transfers, dependent: :destroy
17

×
18
  include SeasonStats
×
19

20
  BUCKET_URL = Rails.application.credentials.aws[:bucket_url].freeze
×
21
  TM_PATH = 'https://www.transfermarkt.com/player-path/profil/spieler/'.freeze
×
22
  NEWBIE_PERIOD = 3.months
×
23

24
  validates :name, presence: true
×
25
  validates :tm_id, uniqueness: true, allow_nil: true
26
  validates :fotmob_id, uniqueness: true, allow_nil: true
×
27

28
  delegate :kit_path, :profile_kit_path, to: :club
×
29

×
30
  COUNTRY = {
×
31
    bo: 'Bolivia',
×
32
    bq: 'Bonaire',
×
33
    ba: 'Bosnia-Herzegovina',
×
34
    cd: 'DR Congo',
×
35
    cv: 'Cape Verde',
×
36
    cz: 'Czech Republic',
×
37
    'gb-eng': 'England',
×
38
    'gb-wls': 'Wales',
×
39
    'gb-sct': 'Scotland',
×
40
    'gb-nir': 'Northern Ireland',
×
41
    gm: 'The Gambia',
×
42
    ir: 'Iran',
×
43
    kr: 'Korea, South',
×
44
    kn: 'St. Kitts & Nevis',
×
45
    lc: 'St. Lucia',
×
46
    md: 'Moldova',
×
47
    ps: 'Palestine',
×
48
    ru: 'terrorist state',
×
49
    sy: 'Syria',
×
50
    tz: 'Tanzania',
×
51
    us: 'United States',
×
52
    ve: 'Venezuela',
×
53
    xk: 'Kosovo'
54
  }.freeze
×
55

×
NEW
56
  scope :by_club, ->(club_id) { where(club_id: club_id) if club_id.present? }
×
57
  scope :search_by_name, lambda { |search_str|
×
58
    where('lower(players.name) LIKE :search OR lower(players.first_name) LIKE :search', search: "%#{search_str.downcase}%")
×
59
  }
×
60
  scope :by_position, ->(position) { joins(:positions).where(positions: { name: position }) if position.present? }
×
61
  scope :by_classic_position, ->(position) { joins(:positions).where(positions: { human_name: position }) if position.present? }
×
62
  scope :by_tournament, ->(tournament) { where(club: tournament.clubs.active) if tournament.present? }
×
63
  scope :by_ec_tournament, ->(tournament) { where(club: tournament.ec_clubs.active) }
×
64
  scope :by_national_tournament, ->(tment_id) { joins(:national_team).where(national_teams: { tournament: tment_id, status: 'active' }) }
×
65
  scope :by_national_teams, ->(nt_id) { where(national_team_id: nt_id) }
×
66
  scope :by_national_tournament_round, ->(tr) { by_national_teams(tr.national_matches.pluck(:host_team_id, :guest_team_id).reduce([], :+)) }
×
67
  scope :by_tournament_round, ->(tr) { by_club(tr.tournament_matches.pluck(:host_club_id, :guest_club_id).reduce([], :+)) }
×
68
  scope :stats_query, -> { includes(:club, :positions).order(:name) }
69
  scope :with_team, -> { includes(:teams).where.not(teams: { id: nil }) }
×
70
  scope :with_admin_includes, -> { includes(:positions, :teams) }
×
71

×
72
  def avatar_path
73
    "#{BUCKET_URL}/player_avatars/#{path_name}.png"
×
74
  end
×
75

×
76
  def profile_avatar_path
77
    "#{BUCKET_URL}/players/#{path_name}.png"
×
78
  end
×
79

80
  def country
×
81
    return '' unless nationality
×
82

83
    COUNTRY[nationality.to_sym] || ISO3166::Country.new(nationality)&.iso_short_name
×
84
  end
×
85

×
86
  def full_name
87
    first_name ? "#{first_name} #{name}" : name
×
88
  end
×
89

×
90
  def newbie?
91
    current_club_joins.any? { |transfer| new_club?(transfer) }
×
92
  end
×
93

×
94
  def full_name_reverse
95
    first_name ? "#{name} #{first_name}" : name
×
96
  end
×
97

×
98
  def full_name_with_positions
99
    "#{full_name} (#{position_names.join(' ')})"
×
100
  end
×
101

×
102
  def pseudo_name
103
    pseudonym.empty? ? full_name : pseudonym
×
104
  end
×
105

×
106
  def path_name
107
    @path_name ||= avatar_name || full_name.downcase.tr(' ', '_').tr('-', '_').delete("'")
×
108
  end
×
109

×
110
  def national_kit_path
111
    national_team&.kit_path || NationalTeam.find_by(code: nationality)&.kit_path
×
112
  end
×
113

114
  def profile_national_kit_path
×
115
    national_team&.profile_kit_path
×
116
  end
117

×
118
  def tm_path
×
119
    return '' unless tm_id
120

×
121
    "#{TM_PATH}#{tm_id}"
×
122
  end
123

×
124
  def tm_position_path(season_start_year)
×
125
    return '' unless tm_id
×
126

127
    "https://www.transfermarkt.com/player-path/leistungsdaten/spieler/#{tm_id}/plus/0?saison=#{season_start_year}"
×
128
  end
×
129

×
130
  def position_names
131
    @position_names ||= positions.map(&:name)
×
132
  end
×
133

×
134
  def position_sequence_number
135
    positions.first&.id || Float::INFINITY
×
136
  end
×
137

138
  def transfer_by(team)
×
139
    transfers.select { |t| t.incoming? && t.team_id == team.id }.last
×
140
  end
141

×
142
  def current_average_price
×
143
    return 0 if teams.blank?
144

×
145
    (teams.map { |team| transfer_by(team)&.price || 0 }.sum(0.0) / teams.count).round(1)
×
146
  end
147

×
148
  def age
×
149
    return if birth_date.empty?
×
150

151
    (Time.zone.today.strftime('%Y%m%d').to_i - birth_date.to_date.strftime('%Y%m%d').to_i) / 10_000
×
152
  end
×
153

×
154
  def team_by_league(league_id)
155
    return teams.find { |t| t.league_id == league_id.to_i } if teams.loaded?
×
156

×
157
    teams.find_by(league_id: league_id)
×
158
  end
×
159

×
160
  def stats_price
×
161
    @stats_price ||= player_season_stats.where(season: Season.second_to_last, tournament: club.tournament).last&.position_price || 1
×
162
  end
×
163

×
164
  def player_bids_by(auction_id)
×
165
    player_bids
166
      .joins('INNER JOIN auction_bids ON player_bids.auction_bid_id = auction_bids.id')
167
      .joins('INNER JOIN auction_rounds ON auction_bids.auction_round_id = auction_rounds.id')
168
      .joins('INNER JOIN auctions ON auction_rounds.auction_id = auctions.id')
×
169
      .includes(auction_bid: %i[auction_round team])
×
170
      .where(auctions: { id: auction_id })
×
171
      .order('player_bids.price DESC')
×
172
      .group_by { |bid| bid.auction_bid.auction_round.number }
×
173
      .sort_by { |round_number, _| round_number }.reverse.to_h
×
174
  end
×
175

×
176
  private
×
177

178
  def current_club_joins
×
179
    club_transfers.select do |transfer|
×
180
      transfer.start_date&.between?(NEWBIE_PERIOD.ago.to_date, Time.zone.today) && current_club?(transfer)
×
181
    end
182
  end
×
183

×
184
  def current_club?(transfer)
×
185
    transfer.new_club_id == club_id || (transfer.new_club_id.nil? && transfer.new_club_name == club&.name)
186
  end
×
187

×
188
  def new_club?(transfer)
×
189
    key = transfer.new_club_id || transfer.new_club_name
190
    return false if key.blank?
×
191

×
192
    club_transfers.none? { |other| recent_return?(other, transfer, key) }
×
193
  end
194

×
195
  def recent_return?(other, transfer, key)
×
196
    return false if other.equal?(transfer) || other.start_date.nil?
×
197
    return false unless other.start_date <= transfer.start_date
198
    return false if other.start_date < transfer.start_date - NEWBIE_PERIOD
×
199

×
200
    [other.new_club_id || other.new_club_name, other.old_club_id || other.old_club_name].include?(key)
×
201
  end
202
end
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc