def test_london_on_demand_two(self): """Test on demand trial data that supposedly had low efficiency""" # yapf: disable demand = [ [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 8, 12, 10, 9, 8, 9, 15, 15, 20, 17, 14, 7, 4], [0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 6, 10, 14, 14, 9, 9, 9, 12, 20, 25, 20, 12, 9, 5], [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 7, 12, 15, 12, 10, 9, 12, 12, 16, 21, 20, 12, 9, 4], [0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 7, 10, 13, 13, 9, 9, 11, 15, 17, 24, 23, 14, 7, 5], [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 7, 10, 13, 13, 11, 11, 11, 12, 25, 29, 29, 20, 12, 9], [6, 0, 0, 0, 0, 0, 0, 0, 3, 6, 6, 12, 19, 18, 17, 16, 16, 18, 24, 25, 25, 18, 11, 7], [4, 0, 0, 0, 0, 0, 0, 0, 3, 6, 7, 12, 19, 19, 17, 16, 16, 18, 23, 29, 26, 14, 9, 5], ] # yapf: enable min_length = 4 max_length = 14 s = Splitter(demand, min_length, max_length) s.calculate() s.validate() # For these, we really need perfect efficiency assert s.efficiency() < 0.01
def _process_task(self, task): # 1. Fetch schedule self.org = self.client.get_organization( task.data.get("organization_id")) self.loc = self.org.get_location(task.data.get("location_id")) self.role = self.loc.get_role(task.data.get("role_id")) self.sched = self.role.get_schedule(task.data.get("schedule_id")) self._compute_demand() self._subtract_existing_shifts_from_demand() # Run the calculation s = Splitter(self.demand, self.sched.data.get("min_shift_length_hour"), self.sched.data.get("max_shift_length_hour")) s.calculate() s.efficiency() # Naive becuase not yet datetimes naive_shifts = s.get_shifts() logger.info("Starting upload of %s shifts", len(naive_shifts)) local_start_time = self._get_local_start_time() for shift in naive_shifts: # We have to think of daylight savings time here, so we need to # guarantee that we don't have any errors. We do this by overshooting # the timedelta by an extra two hours, then rounding back to midnight. logger.debug("Processing shift %s", shift) start_day = normalize_to_midnight( deepcopy(local_start_time) + timedelta(days=shift["day"])) # Beware of time changes - duplicate times are possible try: start = start_day.replace(hour=shift["start"]) except pytz.AmbiguousTimeError: # Randomly pick one. Minor tech debt. start = start_day.replace(hour=shift["start"], is_dst=False) stop = start + timedelta(hours=shift["length"]) # Convert to the strings we are passing up to the cLoUd utc_start_str = start.astimezone(self.default_tz).isoformat() utc_stop_str = stop.astimezone(self.default_tz).isoformat() logger.info("Creating shift with start %s stop %s", start, stop) self.role.create_shift(start=utc_start_str, stop=utc_stop_str)
def test_online_store_prod_failure(self): """This data caused a production error""" # yapf: disable demand = [ [8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8], [8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8], [8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8], [8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8], [8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8], ] # yapf: enable min_length = 8 max_length = 8 s = Splitter(demand, min_length, max_length) s.calculate() s.validate()
def test_call_center(self): """See if it chokes for call center client data""" # yapf: disable demand = [ [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0], ] # yapf: enable min_length = 9 max_length = 9 s = Splitter(demand, min_length, max_length) s.calculate() s.validate() # For these, we really need perfect efficiency assert s.efficiency() < 0.001
def test_la(self): """24/7 LA client data""" # yapf: disable demand = [ # They sometimes have leading zeros [0, 0, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 1], [1, 1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3], [4, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4], [3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4], [4, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], [4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4], ] # yapf: enable min_length = 6 max_length = 8 s = Splitter(demand, min_length, max_length) s.calculate() s.validate() assert s.efficiency() < 0.08
def test_succeeds_with_zero_demand(self): # yapf: disable demand = [ [0]*24, [0]*24, [0]*24, [0]*24, [0]*24, [0]*24, [0]*24, ] # yapf: enable min_length = 4 max_length = 8 s = Splitter(demand, min_length, max_length) s.calculate() s.validate() efficiency = s.efficiency() assert efficiency == PERFECT_OPTIMALITY
def test_on_demand_old(self): """Old on demand data - good test of wrap-around""" # yapf: disable demand = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 4, 4, 6, 6, 7, 7, 5, 2], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 5, 3, 3, 4, 4, 7, 6, 5, 4, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 5, 4, 5, 4, 5, 5, 7, 6, 6, 5, 3], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 5, 5, 2, 5, 6, 7, 6, 6, 5, 3], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 3, 5, 4, 6, 5, 6, 8, 5, 4, 4], [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 6, 5, 2], [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 5, 5, 4, 6, 6, 11, 8, 6, 4, 2], ] # yapf: enable min_length = 4 max_length = 8 s = Splitter(demand, min_length, max_length) s.calculate() s.validate() efficiency = s.efficiency() assert efficiency < EFFICIENCY_LIMIT