YaleSTC/shifts

View on GitHub
lib/tasks/populate.rake

Summary

Maintainability
Test Coverage
namespace :db do
  desc "Erase and fill database"

  task populate: :load_fixtures do
    task_start_time = Time.now

    require 'populator'
    require 'faker'

    # Edit these to change amount of data generated
    # The number of objects of any class can be either an integer or a range
    number_of_users = 10..20
    categories_per_department = 4..6
    stickies_per_department = 50
    announcements_per_department = 50
    # The following range gives start dates for stickies
    notices_start_range = 4.months.ago..4.months.from_now
    loc_groups_per_department = 3..5
    locations_per_loc_group = 2..3
    # If you don't want the priority to be set, (might create fewer shifts), then set the following to nil
    location_priority = 1..5
    max_number_of_shifts_per_time_slot = 20
    # will generate payforms starting at the below date until current; may use relative or absolute time
    payforms_beginning_date = 4.months.ago.to_date
    payform_items_per_payform = 4..6
    # how many day's worth of shifts to generate
    shifts_end_date = 4.days.from_now.to_date


    # Start of code
    puts "emptying database"
    [Department, DepartmentConfig, Category, LocGroup, Location, Payform, PayformItem, TimeSlot, Shift, SubRequest, Notice].each do |model|
      model.delete_all
    end

    puts "creating departments and configuring payform settings"
    Department.create(name: "STC", created_at: 2.years.ago)
      # weekly
    dept = Department.create(name: "Film studies", created_at: 2.years.ago)
      dept.department_config.update_attributes({monthly: true, end_of_month: true}) # monthly
#    dept = Department.create(name: "Economics", created_at: 2.years.ago)
#      dept.department_config.update_attributes({complex: true}) # biweekly
#    dept = Department.create(name: "Political science", created_at: 2.years.ago)
#      dept.department_config.update_attributes({monthly: true, complex: true, day: 16}) # semi-monthly

    puts "creating users and adding users to departments"
    User.populate(number_of_users) do |user|
      user.first_name = Faker::Name.first_name
      user.last_name = Faker::Name.last_name
      user.login = user.first_name.downcase.first + user.last_name.downcase.first + (6 + rand(994)).to_s
      user.email = user.login + "@example.com"
      user.perishable_token = ""
      user.created_at = Department.first.created_at
    end

    User.all.each do |user|
      # Adds users to (possibly multiple) departments
      begin
        Department.all[rand(Department.all.length)].users << user
      end while rand(10) == 0
    end

    Department.all.each do |department|
      puts "creating categories for #{department.name} department"
      Category.populate(categories_per_department) do |category|
        category.name = Populator.words(1..3).titleize
        category.active = true
        category.department_id = department.id
      end

      puts "creating stickies and announcements for #{department.name} department"
      Notice.populate(stickies_per_department) do |sticky|
        sticky.sticky = true
        sticky.content = Populator.sentences(1..3)
        sticky.author_id = department.users[rand(department.users.length)].id
        sticky.start_time = notices_start_range
        end_time = sticky.start_time + (3 + rand(200)).days
        sticky.end_time = end_time > Time.now ? end_time : nil
        sticky.department_id = department.id
        if sticky.end_time
          sticky.remover_id = department.users[rand(department.users.length)].id
        end
        sticky.created_at = sticky.start_time
      end

      Notice.populate(announcements_per_department) do |announcement|
        announcement.sticky = false
        announcement.content = Populator.sentences(1..3)
        announcement.author_id = department.users[rand(department.users.length)].id
        announcement.start_time = notices_start_range
        announcement.end_time = rand(3) == 0 ? nil : announcement.start_time + (3 + rand(200)).days
        announcement.department_id = department.id
        announcement.created_at = announcement.start_time
      end

      puts "creating location groups and locations for #{department.name} department"
      LocGroup.populate(loc_groups_per_department) do |loc_group|
        loc_group.name = Populator.words(1..3).titleize
        loc_group.department_id = department.id
        view_perm = Permission.create(name: loc_group.name + " view")
        loc_group.view_perm_id = view_perm.id
        signup_perm = Permission.create(name: loc_group.name + " signup")
        loc_group.signup_perm_id = signup_perm.id
        admin_perm = Permission.create(name: loc_group.name + " admin")
        loc_group.admin_perm_id = admin_perm.id
        loc_group.created_at = department.created_at

        Location.populate(locations_per_loc_group) do |location|
          location.name = Populator.words(1..3).titleize
          location.short_name = location.name.split.first
          location.min_staff = 0..1
          location.max_staff = location.min_staff + rand(3)
          location.priority = location_priority
          location.active = true
          location.loc_group_id = loc_group.id
          location.created_at = loc_group.created_at
        end
      end
    end

    puts "adding viewers for stickies and announcements"
    Notice.all.each do |notice|
      department = Department.find(notice.department_id)

      if !notice.is_sticky && rand(15) == 0
        notice.departments << department
      else

        begin
          # adds specific users to view each notice
          user = department.users[rand(department.users.length)]
          notice.users << user
        end until rand(10) == 0

        until rand(3) == 0
          # adds locations to a notice
          location = department.locations[rand(department.locations.length)]
          notice.locations << location
        end

        until rand(2) == 0
          # adds location groups to a notice
          loc_group = department.loc_groups[rand(department.loc_groups.length)]
          notice.loc_groups << loc_group
        end

      end
    end

    puts "creating payforms and payform items from #{payforms_beginning_date} to now"
    User.all.each do |user|
      puts "\tfor #{user.name}"
      date = Date.today
      while date > payforms_beginning_date
        user.departments.each do |department|
          end_date = Payform.default_period_date(date, department)
          unless Payform.exists?({date: end_date, department_id: department.id, user_id: user.id})
            payform = Payform.build(department, user, date)

            PayformItem.populate(payform_items_per_payform) do |payform_item|
              categories = department.categories.all
              payform_item.category_id = categories[rand(categories.length)].id
              payform_item.user_id = user.id
              payform_item.payform_id = payform.id
              payform_item.active = true
              hours = [0.5]
              while hours.last < 7.0
                hours << (hours.last + 0.1).round(1)
              end
              # each payform's hours range from 0.5 to 7.0, with increments of 0.1
              payform_item.hours = hours
              payform_item.date = payform.start_date..end_date
              payform_item.description = Populator.sentences(1..3)
            end

            if payform.date < Date.today && rand(4) != 0
              payform.update_attribute(:submitted, (payform.date + 1.day).to_time)
              unless rand(3) == 0
                payform.update_attributes({approved: payform.submitted + 1.day,
                                           approved_by_id: department.users[rand(department.users.length)].id
                })
                if rand(2) == 0
                  payform.update_attribute(:printed, payform.approved + 10.minutes)
                end
              end
            end

          end
        end
        date -= 7.days #decrements date
      end
    end


# For each department, for each day from now until some time in the future, 1 time slot is created from 9AM to 11PM
    puts "creating a timeslot from 9AM to 11PM and populating the timeslot with shifts and sub requests"
    Department.all.each do |department|
      increment = department.department_config.time_increment
      raise "this department's time increment does not divide into 60" unless 60 % increment == 0
      blocks_per_hour = 60 / increment
      increment_seconds = increment * 60

      department.loc_groups.all.each do |loc_group|
        loc_group.locations.all.each do |location|
          (Date.today..shifts_end_date).each do |day|
            puts "\tfor department #{department.name}, location #{location.short_name}, on #{day}"

            start_time = Time.parse("9AM -0400 #{day.to_s}")
            end_time = Time.parse("11PM -0400 #{day.to_s}")
            TimeSlot.create(location_id: location.id, start: start_time,
                            end: end_time, created_at: day.to_datetime)

            max_number_of_shifts_per_time_slot.times do
              start_hour = 9 + rand(14)
              start_minute = increment * rand(blocks_per_hour)
              start_time = "#{start_hour}:#{start_minute}, #{day}".to_time
              end_time = start_time + (increment * (1 + rand(6 * blocks_per_hour))).minutes
              user = department.users[rand(department.users.length)]
              shift = Shift.new(start: start_time, end: end_time, user_id: user.id,
                                location_id: location.id, scheduled: true,
                                created_at: day.to_datetime + 30.minutes)
              shift.save unless shift.exceeds_max_staff?
              if !shift.new_record? && rand(10) == 0
                # shift_chunks is the number of smallest shift chunks in the length of the shift
                # this next bit is confusing so don't mess with it
                shift_chunks = ((shift.end - shift.start) / increment_seconds).round
                start_time = shift.start + (increment * rand(shift_chunks)).minutes
                chunks_left = ((shift.end - start_time) / increment_seconds).round
                end_time = start_time + (increment * (1 + rand(chunks_left))).minutes
                request_chunks = ((end_time - start_time) / increment_seconds).round
                mandatory_start = start_time + (increment * rand(request_chunks)).minutes
                request_chunks_left = ((end_time - mandatory_start) / increment_seconds).round
                mandatory_end = mandatory_start + (increment * (1 + rand(request_chunks_left))).minutes
                sub_request = SubRequest.create(shift_id: shift.id, reason: Populator.sentences(1..3),
                                                start: start_time, end: end_time,
                                                mandatory_start: mandatory_start,
                                                mandatory_end: mandatory_end)
                # each sub request has 1/2 chance of being taken
                if !sub_request.new_record? && rand(2) == 0
                  user = department.users[rand(department.users.length)]
                  begin
                    SubRequest.take(sub_request, user, rand(2) == 0 ? true : false)
                  rescue
                    puts "An exception was raised while trying to take the sub request\n#{$!}"
                  end
                end
              end
            end
          end
        end
      end
    end

    def length_of_time_to_s(seconds)
      seconds = seconds.round
      return "#{seconds} sec" if seconds / 60 < 1
      minutes = seconds / 60
      seconds = seconds % 60
      return "#{minutes} min #{seconds} sec" if minutes / 60 < 1
      hours = minutes / 60
      minutes = minutes % 60
      return "#{hours} hr #{minutes} min #{seconds} sec" if hours / 24 < 2
      days = hours / 24
      hours = hours % 24
      return "#{days} days #{hours} hr #{minutes} min #{seconds} sec"
    end

    puts "rake task completed at #{Time.now}"
    puts "runtime: #{length_of_time_to_s(Time.now - task_start_time)}"
  end
end