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

codebar / planner / 16886378175

11 Aug 2025 04:43PM UTC coverage: 95.141% (+0.03%) from 95.111%
16886378175

Pull #2260

github

web-flow
Merge 8f8a934b9 into f06a3d13f
Pull Request #2260: Member search

22 of 22 new or added lines in 3 files covered. (100.0%)

1 existing line in 1 file now uncovered.

3231 of 3396 relevant lines covered (95.14%)

91.91 hits per line

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

95.06
/app/models/member.rb
1
class Member < ApplicationRecord
2✔
2
  include Permissions
2✔
3

4
  has_many :attendance_warnings
2✔
5
  has_many :bans
2✔
6
  has_many :eligibility_inquiries
2✔
7
  has_many :workshop_invitations
2✔
8
  has_many :invitations
2✔
9
  has_many :auth_services
2✔
10
  has_many :feedbacks, foreign_key: :coach_id
2✔
11
  has_many :subscriptions
2✔
12
  has_many :groups, through: :subscriptions
2✔
13
  has_many :member_notes
2✔
14
  has_many :chapters, -> { distinct }, through: :groups
26✔
15
  has_many :announcements, -> { distinct }, through: :groups
402✔
16
  has_many :meeting_invitations
2✔
17

18
  validates :auth_services, presence: true
2✔
19
  validates :name, :surname, :email, :about_you, presence: true, if: :can_log_in?
2✔
20
  validates :email, uniqueness: true
2✔
21
  validates :about_you, length: { maximum: 255 }
2✔
22

23
  scope :accepted_toc, -> { where.not(accepted_toc_at: nil) }
44✔
24
  scope :order_by_email, -> { order(:email) }
6✔
25
  scope :subscribers, -> { joins(:subscriptions).order('created_at desc').uniq }
2✔
26
  scope :not_banned, lambda {
2✔
27
                       joins('LEFT OUTER JOIN bans ON members.id = bans.member_id')
1,812✔
28
                         .where('bans.id is NULL or bans.expires_at < CURRENT_DATE')
29
                     }
30
  scope :attending_meeting, lambda { |meeting|
2✔
31
                              not_banned
6✔
32
                                .joins(:meeting_invitations)
33
                                .where('meeting_invitations.meeting_id = ? and meeting_invitations.attending = ?',
34
                                       meeting.id, true)
35
                            }
36
  scope :in_group, lambda { |members|
2✔
37
    not_banned.accepted_toc.joins(:groups).where(groups: { id: members.select(:id) })
42✔
38
  }
39

40
  scope :with_skill, ->(skill_name) { tagged_with(skill_name) }
4✔
41

42
  acts_as_taggable_on :skills
2✔
43

44
  attr_accessor :attendance, :newsletter
2✔
45

46
  def banned?
2✔
47
    bans.active.present? || bans.permanent.present?
234✔
48
  end
49

50
  def banned_permanently?
2✔
51
    bans.permanent.present?
×
52
  end
53

54
  def name_and_surname
2✔
55
    [name, surname].compact.join ' '
2,696✔
56
  end
57

58
  def full_name
2✔
59
    pronoun = pronouns.present? ? "(#{pronouns})" : nil
2,680✔
60
    [name_and_surname, pronoun].compact.join ' '
2,680✔
61
  end
62

63
  def student?
2✔
64
    groups.students.any?
156✔
65
  end
66

67
  def coach?
2✔
68
    groups.coaches.any?
102✔
69
  end
70

71
  def received_welcome_for?(subscription)
2✔
72
    return received_student_welcome_email if subscription.student?
26✔
73
    return received_coach_welcome_email if subscription.coach?
10✔
74

75
    true
×
76
  end
77

78
  def avatar(size = 100)
2✔
79
    "https://secure.gravatar.com/avatar/#{md5_email}?size=#{size}&default=identicon"
708✔
80
  end
81

82
  def requires_additional_details?
2✔
83
    can_log_in? && !valid?
72✔
84
  end
85

86
  def has_existing_RSVP_on(date)
2✔
87
    invitations_on(date).any?
48✔
88
  end
89

90
  def already_attending(event)
2✔
91
    invitations.where(attending: true).map { |e| e.event.id }.include?(event.id)
8✔
92
  end
93

94
  def upcoming_rsvps
2✔
95
    @upcoming_rsvps ||= rsvps(period: :upcoming)
24✔
96
  end
97

98
  def past_rsvps
2✔
99
    @past_rsvps ||= rsvps(period: :past).reverse
×
100
  end
101

102
  def flag_to_organisers?
2✔
103
    multiple_no_shows? && attendance_warnings.last_six_months.length >= 2
8✔
104
  end
105

106
  def multiple_no_shows?
2✔
107
    last_six_month_rsvps = workshop_invitations.taken_place.last_six_months.accepted
12✔
108
    (last_six_month_rsvps.length - last_six_month_rsvps.attended.length) > 3
12✔
109
  end
110

111
  def recent_notes
2✔
112
    last_five_workshops = workshop_invitations.order_by_latest.attended.take(5)
30✔
113
    return [] if last_five_workshops.empty?
30✔
114

115
    # Take 1 day out to include notes added on the previous day of the workshop
116
    notes_from_date = last_five_workshops.last.workshop.date_and_time - 1.day
4✔
117

118
    member_notes.where('created_at > ?', notes_from_date)
4✔
119
  end
120

121
  def self.find_members(name)
2✔
122
    where('name ILIKE ?', "%#{name}%").or(where('surname ILIKE ?', "%#{name}%"))
2✔
123
  end
124

125
  private
2✔
126

127
  def invitations_on(date)
2✔
128
    workshop_invitations
48✔
129
      .joins(:workshop)
130
      .where('workshops.date_and_time BETWEEN ? AND ?', date.beginning_of_day, date.end_of_day)
131
      .where(attending: true)
132
  end
133

134
  def md5_email
2✔
135
    Digest::MD5.hexdigest(email.strip.downcase)
708✔
136
  end
137

138
  def rsvps(period:)
2✔
139
    time_period = "#{period}_rsvps"
24✔
140

141
    [invitations.joins(:event),
24✔
142
     workshop_invitations.joins(:workshop).includes(workshop: :chapter),
UNCOV
143
     meeting_invitations.joins(:meeting)].map(&time_period.to_sym).inject(:+).sort_by { |i| i.event.date_and_time }
×
144
  end
145
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc