diff --git a/app/models/chapter.rb b/app/models/chapter.rb index ae0f9d9bd..d28f497a6 100644 --- a/app/models/chapter.rb +++ b/app/models/chapter.rb @@ -48,6 +48,14 @@ def coaches .distinct end + def eligible_students + Member.in_group(groups.students).distinct + end + + def eligible_coaches + Member.in_group(groups.coaches).distinct + end + private def expire_chapters_sidebar_cache diff --git a/app/models/group.rb b/app/models/group.rb index e57bc93fc..56cb4506e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -20,4 +20,8 @@ class Group < ApplicationRecord def to_s "#{name} #{chapter.name}" end + + def eligible_members + members.not_banned.accepted_toc.distinct + end end diff --git a/app/views/admin/chapters/show.html.haml b/app/views/admin/chapters/show.html.haml index f2f3bc13f..198ab4577 100644 --- a/app/views/admin/chapters/show.html.haml +++ b/app/views/admin/chapters/show.html.haml @@ -30,7 +30,7 @@ - @groups.each do |group| %li.nav-item = link_to [ :admin, group ], class: 'nav-link' do - #{group.name} (#{group.members.count}) + #{group.name} (#{group.eligible_members.count} eligible, #{group.members.count} total) %li.nav-item = link_to admin_chapter_members_path(@chapter, type: group.name.downcase), class: 'nav-link' do View #{group.name} emails diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 8473ae90b..e7fc1ce68 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -7,7 +7,7 @@ .row .col - %h3.mb-3 Members (#{@group.members.count}) + %h3.mb-3 Members (#{@group.eligible_members.count} eligible, #{@group.members.count} total) %table.table.table-striped.table-hover %tbody - @group.members.each do |member| diff --git a/spec/models/chapter_spec.rb b/spec/models/chapter_spec.rb index 575685425..a20445d2a 100644 --- a/spec/models/chapter_spec.rb +++ b/spec/models/chapter_spec.rb @@ -1,9 +1,9 @@ RSpec.describe Chapter do - it { should validate_presence_of(:city) } - it { should validate_length_of(:description).is_at_most(280) } + it { is_expected.to validate_presence_of(:city) } + it { is_expected.to validate_length_of(:description).is_at_most(280) } context 'validations' do - context '#slug' do + describe '#slug' do it 'a chapter must have a slug set' do chapter = Chapter.new(name: 'London', city: 'London', email: 'london@codebar.io') chapter.save @@ -19,7 +19,7 @@ end end - context '#time_zone' do + describe '#time_zone' do it 'requires a time zone' do chapter = Fabricate(:chapter) expect(chapter).to be_valid @@ -32,7 +32,7 @@ end context 'scopes' do - context '#active' do + describe '#active' do it 'only returns active Chapters' do 1.times { Fabricate(:chapter) } 2.times { Fabricate(:chapter, active: false) } @@ -65,4 +65,40 @@ expect(Rails.cache.read(cache_key)).to be_nil end end + + describe '#eligible_students' do + let(:chapter) { Fabricate(:chapter) } + let(:student_group) { Fabricate(:group, chapter: chapter, name: 'Students') } + + it 'includes only students with accepted TOC who are not banned' do + eligible_student = Fabricate(:member, groups: [student_group], accepted_toc_at: Time.zone.now) + _ineligible_no_toc = Fabricate(:member, groups: [student_group], accepted_toc_at: nil) + _ineligible_banned = Fabricate(:banned_member, groups: [student_group], accepted_toc_at: Time.zone.now) + + expect(chapter.eligible_students).to contain_exactly(eligible_student) + end + + it 'returns empty relation when no eligible students' do + Fabricate(:member, groups: [student_group], accepted_toc_at: nil) + expect(chapter.eligible_students).to be_empty + end + end + + describe '#eligible_coaches' do + let(:chapter) { Fabricate(:chapter) } + let(:coach_group) { Fabricate(:group, chapter: chapter, name: 'Coaches') } + + it 'includes only coaches with accepted TOC who are not banned' do + eligible_coach = Fabricate(:member, groups: [coach_group], accepted_toc_at: Time.zone.now) + _ineligible_no_toc = Fabricate(:member, groups: [coach_group], accepted_toc_at: nil) + _ineligible_banned = Fabricate(:banned_member, groups: [coach_group], accepted_toc_at: Time.zone.now) + + expect(chapter.eligible_coaches).to contain_exactly(eligible_coach) + end + + it 'returns empty relation when no eligible coaches' do + Fabricate(:member, groups: [coach_group], accepted_toc_at: nil) + expect(chapter.eligible_coaches).to be_empty + end + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 810ae6595..78dbe31c8 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -2,12 +2,29 @@ subject(:group) { Fabricate.build(:group) } context 'validations' do - it { should validate_presence_of(:name) } + it { is_expected.to validate_presence_of(:name) } it do - should validate_inclusion_of(:name) + expect(subject).to validate_inclusion_of(:name) .in_array(%w[Coaches Students]) .with_message('Invalid name for Group') end end + + describe '#eligible_members' do + let(:group) { Fabricate(:group, name: 'Students') } + + it 'includes only members with accepted TOC who are not banned' do + eligible_member = Fabricate(:member, groups: [group], accepted_toc_at: Time.zone.now) + _ineligible_no_toc = Fabricate(:member, groups: [group], accepted_toc_at: nil) + _ineligible_banned = Fabricate(:banned_member, groups: [group], accepted_toc_at: Time.zone.now) + + expect(group.eligible_members).to contain_exactly(eligible_member) + end + + it 'returns empty relation when no eligible members' do + Fabricate(:member, groups: [group], accepted_toc_at: nil) + expect(group.eligible_members).to be_empty + end + end end