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:

  1. 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.

  2. 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!

  3. 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

Popular posts from this blog

asynchronous - C# WinSCP .NET assembly: How to upload multiple files asynchronously -

aws api gateway - SerializationException in posting new Records via Dynamodb Proxy Service in API -

asp.net - Problems sending emails from forum -