Completed
Push — more-scheduling-tools ( bfeaad )
by
unknown
01:59
created

Context.person()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
module Scheduling
2
  # ActiveModel access is slow enough that we create a stripped-down, in-memory version of the various
3
  # models we need to create a schedule, then run the annealer against this in-memory model.
4
  # This class sucks all the sessions and timeslots from the DB, and provides them during annealing.
5
  #
6
  # A Context, its Person objects, and their SessionSets do _not_ change during annealing.
7
  # The Schedule class contains all the state we're trying to optimize.
8
  #
9
  class Context
10
    attr_reader :sessions, :timeslots, :room_count
11
12
    def initialize(event)
13
      @timeslots = event.timeslots.where(schedulable: true)
14
      @room_count = event.rooms.where(schedulable: true).count
15
16
      # A presenter can have a session that we're manually keeping out of the schedule by making it
17
      # manually_scheduled and assigning either no timeslot (like Indie Arcade) or an unschedulable
18
      # timeslot (like Session 0). The scoring system would count that as an unresolvable problem
19
      # for the presenter, since it wants every one of their sessions to be in a schedulable timeslot.
20
      # We therefore ignore those sessions altogether for scheduling purposes.
21
      #
22
      @sessions = event.sessions
23
        .where("not manually_scheduled or timeslot_id in (?)", @timeslots.map(&:id))
24
        .pluck(:id)
25
26
      @people_by_id = Hash.new { |h,id| h[id] = Person.new(self, id) }
27
28
      load_sets :attending,  Attendance
29
      load_sets :presenting, Presentation
30
31
      report_count :attending
32
      report_count :presenting
33
34
      raise 'No session-presenter relationships in DB. Did you populate the presentations table?' unless people.size > 0
35
36
      event.presenter_timeslot_restrictions.each do |restriction|
37
        person(restriction.participant_id).
38
          assign_timeslot_penalty(restriction.timeslot_id, restriction.weight)
39
      end
40
    end
41
42
    def people
43
      @people_by_id.values
44
    end
45
46
  private
47
48
    def person(id)
49
      @people_by_id[id]
50
    end
51
52
    # @param [Symbol] role
53
    # @param [Class] either Attendance or Presentation
54
    def load_sets(role, association_model)
55
      # This brute force iteration is hardly slick, but I'm too rusty on fancy ActiveRecord querying to care just now. -PPC
56
      size = association_model.where(session_id: @sessions).select(:participant_id, :session_id).each do |assoc|
57
        person(assoc.participant_id).
58
          send(role).
59
          add(assoc.session_id)
60
      end.size
61
    end
62
63
    def report_count(role)
64
      assoc_count = people.map { |p| p.send(role).size }.sum
65
      person_count = people.count { |p| p.send(role).size > 0 }
66
      puts "#{assoc_count} #{role.to_s.humanize.downcase} relationships" +
67
           " (#{person_count} people, avg #{"%1.1f" % (assoc_count / person_count.to_f)} each)"
68
    end
69
  end
70
end
71