Completed
Push — feature/add-more-attributes-to... ( e662dc...b821bf )
by
unknown
02:12
created

Session   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 156
Duplicated Lines 0 %
Metric Value
dl 0
loc 156
rs 10
wmc 15

12 Methods

Rating   Name   Duplication   Size   Complexity  
A swap_rooms() 0 11 2
A swap_timeslot_and_rooms() 0 8 1
A attendee_preferences() 0 16 1
A session_similarity() 0 6 1
A presenter_names() 0 3 1
A other_presenter_names() 0 3 1
A estimated_interest() 0 20 1
A to_h() 0 3 1
A recommended_sessions() 0 14 2
A other_presenters() 0 3 1
A attending? 0 5 2
A create_presenter() 0 3 1
1
class Session < ActiveRecord::Base
2
3
  has_many :categorizations, :dependent => :destroy
4
  has_many :categories, :through => :categorizations
5
  belongs_to :participant  # TODO: rename to 'owner'
6
7
  has_many :presentations, :dependent => :destroy
8
  has_many :presenters, :through => :presentations, :source => :participant
9
  belongs_to :event
10
  belongs_to :timeslot
11
  belongs_to :room
12
  belongs_to :level
13
  has_many :attendances, :dependent => :destroy
14
  has_many :participants, :through => :attendances
15
16
  delegate :name, to: :room, prefix: true, allow_nil: true
17
  delegate :starts_at, to: :timeslot, allow_nil: true
18
  delegate :name, to: :level, prefix: true, allow_nil: true
19
20
  scope :with_attendence_count, -> { select('*').joins("LEFT OUTER JOIN (SELECT session_id, count(id) AS attendence_count FROM attendances GROUP BY session_id) AS attendence_aggregation ON attendence_aggregation.session_id = sessions.id") }
21
22
  scope :for_current_event, lambda { where(event_id: Event.current_event.id) }
23
24
  validates_presence_of :description
25
  validates_presence_of :event_id
26
  validates_presence_of :participant_id
27
  validates_presence_of :title
28
  validates_length_of :summary, :maximum => 100, :allow_blank => true
29
  #validates_uniqueness_of :timeslot_id, :scope => :room_id, :allow_blank => true, :message => 'and room combination already in use'
30
31
32
  attr_accessor :name, :email
33
34
  after_create :create_presenter
35
36
  def self.swap_timeslot_and_rooms(session_1, session_2)
37
    Session.transaction do
38
      session_1.room, session_2.room = session_2.room, session_1.room
39
      session_1.timeslot, session_2.timeslot = session_2.timeslot, session_1.timeslot
40
      session_1.save
41
      session_2.save
42
    end
43
  end
44
45
  def self.swap_rooms(session_1, session_2)
46
    if session_1.timeslot != session_2.timeslot
47
      raise "Sessions must be in the same timeslot to swap"
48
    end
49
50
    Session.transaction do
51
      session_1.room, session_2.room = session_2.room, session_1.room
52
      session_1.save
53
      session_2.save
54
    end
55
  end
56
57
  def self.attendee_preferences
58
    result = {}
59
    sessions = Event.current_event.sessions.includes(:participants)
60
61
    sessions.each do |session|
62
      prefs = {}
63
64
      session.participant_ids.each do |p_id|
65
        prefs[p_id] = 1
66
      end
67
68
      result[session.id] = prefs
69
    end
70
71
    result
72
  end
73
74
75
  def self.session_similarity()
76
    Rails.cache.fetch('session_similarity', :expires_in => 30.minutes) do
77
      preferences = Session.attendee_preferences
78
      ::Recommender.calculate_similar_items(preferences, 5)
79
    end
80
  end
81
82
  def presenter_names
83
    presenters.map(&:name)
84
  end
85
86
  def other_presenters
87
    presenters.reject{ |p| p == self.participant }
88
  end
89
  def other_presenter_names
90
    other_presenters.map(&:name)
91
  end
92
93
94
  def attending?(user)
95
    return false if user.nil?
96
97
    participants.include?(user)
98
  end
99
100
  def recommended_sessions
101
    similarity = Session.session_similarity()
102
    recommended = similarity[self.id]
103
104
    if recommended
105
      # find will not order by recommendation strength; use conditions instead of find to ignore missing sessions in the cache
106
      sessions = Session.where(["id in (?)", recommended.map { |r| r[1] }])
107
      sessions.sort_by do |session|
108
        recommended.find_index { |r| r[1] == session.id }
109
      end
110
    else
111
      []
112
    end
113
  end
114
115
  # Estimates actual event-day interest for this session relative to other sessions,
116
  # expressed as a corrected number of votes.
117
  #
118
  # Sessions that were created earlier tend to accumulate more votes, so the naive method
119
  # of using raw vote count will underestimate interest in sessions created later.
120
  # To fix that, we take the number of votes this session received as a proportion of all
121
  # votes cast since it was created. We also include a normalizing factor for last-minute
122
  # sessions created after most of the voting was already done.
123
  #
124
  def estimated_interest
125
    @estimated_interest ||= begin
126
      session_votes     = attendances.count.to_f
127
      possible_votes    = event.attendances.where('attendances.created_at >= ?', created_at).count.to_f
128
      session_count     = event.sessions.count.to_f
129
      participant_count = event.participants.count.to_f
130
131
      # For sessions created at the last minute, we don't have enough information to make
132
      # a good estimate; both session_votes and possible_votes are too low. If we just divide
133
      # session_votes / possible_votes, we'll get wildly inaccurate answers when the denominator
134
      # is small.
135
      #
136
      # We therefore add some ghost "ballast votes" across the board to all sessions, so as to
137
      # make estimated_interest tend toward the mean in cases when there are few real votes.
138
139
      ballast_votes = 3.0
140
141
      (session_votes + ballast_votes) / (possible_votes + ballast_votes * session_count) * participant_count
142
    end
143
  end
144
145
  def to_h
146
    SessionsJsonBuilder.new.to_hash(self)
147
  end
148
149
  private
150
151
  # assign the creator as the first presenter
152
  def create_presenter
153
    presentations.create(participant: participant)
154
  end
155
156
end
157