ruby on rails - Automatically Scheduling Conference -
i'm trying create ruby on rails site manages conferences. should fill in time slots without gaps in between. i've got point fill in the slots. in instances leaves time slots empty. i'm not able find flow in logic.
app/services/conference_service.rb
class conferenceservice def initialize(conference, temp_file) self.first_track = conference.tracks.first self.second_track = conference.tracks.last self.file = temp_file self.talks = [] end def call create_talks set_track(1, 'lunch') set_track(2, 'lunch') set_track(1, 'networking event') # set_track(2, 'networking event') set_second_track_evening end private def create_talks file.read.split(/\n/).each |line| next if line.blank? title = line.split(/\d|lightning/).first length = line.scan(/\d+/).first length = length.nil? ? 5 : length.to_i talks << talk.create(title: title, length: length) end end attr_accessor :first_track, :second_track, :file, :talks def set_track(track_number, track_portion) track = track_number == 1 ? first_track : second_track time = track_portion == 'lunch' ? time.zone.now.change(hour: 9) : time.zone.now.change(hour: 13) minutes = track_portion == 'lunch' ? 180 : 240 talks.shuffle! local_talks = [] n = 0 while local_talks.map(&:length).inject(0, &:+) < minutes local_talks << talks[n] n += 1 end if local_talks.map(&:length).inject(0, &:+) == minutes local_talks.each |talk| talk.start_time = time track.talks << talk time = time.advance(minutes: talk.length) end track.talks << talk.create(title: track_portion, start_time: time, length: 60) track.save (0..local_talks.count - 1).each |i| talks.delete_at(i) end else set_track(track_number, track_portion) end end def set_second_track_evening time = time.zone.now.change(hour: 13) talks.each |talk| talk.start_time = time time = time.advance(minutes: talk.length) end second_track.talks << talks second_track.talks << talk.create(title: 'networking event', start_time: time.change(hour: 17), length: 60) end end
app/controllers/conference_controller.rb
def create @conference = conference.new(conference_params) build_tracks conference_service = conferenceservice.new(@conference, input_file) conference_service.call respond_to |format| if @conference.save format.html { redirect_to @conference, notice: 'conference created.' } format.json { render :show, status: :created, location: @conference } else format.html { render :new } format.json { render json: @conference.errors, status: :unprocessable_entity } end end end def input_file params['conference']['input_file'] end
input file
writing fast tests against enterprise rails 60min overdoing in python 45min lua masses 30min ruby errors mismatched gem versions 45min common ruby errors 45min rails python developers lightning communicating on distance 60min accounting-driven development 45min woah 30min sit down , write 30min pair programming vs noise 45min rails magic 60min ruby on rails: why should move on 60min clojure ate scala (on project) 45min programming in boondocks of seattle 30min ruby vs. clojure back-end development 30min ruby on rails legacy app maintenance 60min world without hackernews 30min user interface css in rails apps 30min
error when calling set_track(2, 'networking event')
undefined method `length' nil:nilclass #line 42
recommend few things before worrying algorithm:
separate concerns / single responsibility. code parses file should independent code runs business logic, should independent code saves database. separating these things may seem unnecessary simple logic (and may be), necessary app complexity grows.
write tests. refactor code, you're going want ensure still works. bonus: writing code can test forces create interfaces can understand, can make code easier understand!
come design first. reading code have no idea intention of sections are. 1 of favorite ways use class, responsibilities, collaborators post cards (see https://en.wikipedia.org/wiki/class-responsibility-collaboration_card , http://agilemodeling.com/artifacts/crcmodel.htm).
it seems break code down into:
- parse input file generic 'talk' objects have length (in minutes) , name. not have these db backed. if it's same concept activerecord model, name
talkdouble
(or similar). i'd recommend using csv here rather own custom (and hard parse) format. - schedule talk objects tracks. seems you're trying randomize talks across 2 tracks, built-in lunch breaks (?). whatever desired behavior, doesn't need use plain old ruby objects. i've found best have logic stateless/idempotent , return new object each time it's run result.
for example:
class talkscheduler def schedule(talks, number_of_tracks: 2) # logic goes here, returns array of `tracks` # each set of talks. tracks = build_tracks(number_of_tracks) talks.each |talk| tracks.sample.add_talk(talk) end tracks end def build_tracks(number) (0..number).times.map { track.new } end end
however, if you're looking algorithm chooses "best fit" of available talks open spaces, you're trying solve knapsack problem (https://en.wikipedia.org/wiki/knapsack_problem). may not become combinatorially hard due limited number of talk lengths (e.g. 30, 45 , 60) realize you're slipping challenging territory.
i'd question value of ability create conference random order of talks vs. being able organize them hand.
in case, handle solving problem of determining (random?) selection of talks in given time-space following:
class schedule slot_length = 15 attr_accessor :start, :length, :talks def initialize(start:, length:) @start = start @length = length @slots = length / slot_length @talks = [] end def add_talk(talk) talks.push(talk) end def slots_remaining slots - talks.map(&:length).sum / slot_length end def can_fit?(talk) talk.length / slot_length <= slots_remaining end end class talkscheduler def schedule(talks, schedules) unscheduled_talks = talks.dup.shuffle # dup, if don't shuffle schedules.each |schedule| while(talks.any?) index = unscheduled_talks.index{|t| schedule.can_fit?(t) } break unless index talk = unscheduled_talks.delete_at(index) schedule.add_talk(talk) end end end end
i'd think bit more model lunches, networking breaks, etc. before deciding model them talks or else, using type of pattern (simple ruby objects store data being manipulated nounverber
classes contain complex business logic) has been helpful me simplifying handling complex workflows you're doing here.
good luck!
Comments
Post a Comment