def test_filter_on_pending(self): request_id = 5 r1 = Request( configurations=None, windows=Windows(), request_id=request_id, state='PENDING' ) r2 = Request( configurations=None, windows=Windows(), request_id=9, state='UNSCHEDULABLE' ) rg1 = RequestGroup( operator='single', requests=[r1, r2], proposal=None, expires=None, rg_id=1, is_staff=False, name=None, ipp_value=1.0, observation_type='NORMAL', submitter='', ) filter_on_pending([rg1]) assert_equal(rg1.requests, [r1])
def test_has_windows_windows(self): window_dict = { 'start': "2013-03-01T00:00:00Z", 'end': "2013-03-01T00:30:00Z", } w = Window(window_dict=window_dict, resource=self.t1['name']) windows = Windows() windows.append(w) assert_equal(windows.has_windows(), True)
def create_request_group(window_dicts, operator='and', resource_name='Martin', configurations=None, proposal=create_mock_proposal(), expires=None, duration=60, first_request_id=5, request_group_id=5): t1 = {'name': resource_name} req_list = [] window_list = [] next_request_id = int(first_request_id) for req_windows in window_dicts: windows = Windows() for window_dict in req_windows: w = Window(window_dict=window_dict, resource=t1['name']) windows.append(w) window_list.append(w) r = Request(configurations=configurations, windows=windows, request_id=next_request_id) r.get_duration = Mock(return_value=duration) req_list.append(r) next_request_id += 1 if len(req_list) == 1: operator = 'single' if expires: RequestGroup.expires = PropertyMock(return_value=expires) else: RequestGroup.expires = PropertyMock(return_value=datetime.utcnow() + timedelta(days=365)) rg = RequestGroup( operator=operator, requests=req_list, proposal=proposal, expires=None, rg_id=request_group_id, is_staff=False, name=None, ipp_value=1.0, observation_type='NORMAL', submitter='', ) return rg, window_list
def intervals_to_windows(req, intersections_for_resource): windows = Windows() for resource_name, intervals in intersections_for_resource.items(): windows_for_resource = req.windows.windows_for_resource[resource_name] # TODO: This needs cleanup # It's possible there are no windows for this resource so we can't # assume that we will be able to get a handle on the resource from the # first window. if (len(windows_for_resource) > 0): resource = windows_for_resource[0].resource for (start, end) in intervals.toTupleList(): w = Window({'start': start, 'end': end}, resource) windows.append(w) return windows
def test_large_and_requests(self): days_out = 0 # build up a request a day for 100 days out new_time = datetime(2016, 10, 3, 5, 0) request_list = [] while days_out < 80: resource = '1m0a.doma.ogg' window = Window( { 'start': new_time + timedelta(days=days_out), 'end': new_time + timedelta(days=days_out, hours=0, minutes=30) }, resource) windows = Windows() windows.append(window) request = Request(configurations=[self.configuration], windows=windows, request_id=int("11{}".format(days_out).rjust( 10, '0')), duration=1750) request_list.append(request) days_out += 1 request_group = RequestGroup(operator='and', requests=request_list, proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=100, is_staff=False, ipp_value=1.0, name='large ur', submitter='', observation_type='NORMAL') normal_request_list = [ request_group, ] result = self._schedule_requests([], normal_request_list, new_time - timedelta(hours=10)) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # assert that none of the and is scheduled (since it has an unschedulable request in it) # assert that both of the manys are scheduled assert 100 in scheduled_rgs for req in request_list: # assert each child request is in the schedule (scheduler schedules past horizon for ands) assert req.id in scheduled_rgs[100]
def test_multiple_user_intervals_are_honoured(self): # A one day user supplied window windows = [{ 'start': datetime(2011, 11, 1, 6, 0, 0), 'end': datetime(2011, 11, 1, 9, 0, 0) }, { 'start': datetime(2011, 11, 2, 1, 0, 0), 'end': datetime(2011, 11, 2, 4, 0, 0) }] dt_windows = Windows() resource_name = '1m0a.doma.bpl' for w in windows: dt_windows.append(Window(w, self.tels[resource_name]['name'])) req = Request(configurations=[self.configuration], windows=dt_windows, request_id='1') visibilities = construct_visibilities(self.tels, self.start, self.end) intervals_for_resource = self.make_rise_set_intervals( req, visibilities) compute_request_availability(req, intervals_for_resource, {}) received = req_windows_to_kernel_intervals( req.windows.windows_for_resource) # The user windows constrain the available observing windows (compare to # previous tests) date_format = '%Y-%m-%d %H:%M:%S.%f' rise_set_dark_intervals = ( datetime.strptime('2011-11-01 06:00:00.0', date_format), datetime.strptime('2011-11-01 07:52:00.564199', date_format), datetime.strptime('2011-11-02 02:01:50.423880', date_format), datetime.strptime('2011-11-02 04:00:00.0', date_format), ) # Verify we get the intervals we expect for resource_name, received_intervals in received.items(): for i, received_tp in enumerate(received_intervals.toDictList()): assert_equal(received_tp['time'], rise_set_dark_intervals[i])
def make_constrained_request(self, airmass=None, start=datetime(2011, 11, 1, 6, 0, 0), end=datetime(2011, 11, 2, 6, 0, 0)): # A one day user supplied window window_dict = {'start': start, 'end': end} resource_name = '1m0a.doma.bpl' resource = self.tels[resource_name] window = Window(window_dict, resource['name']) dt_windows = Windows() dt_windows.append(window) configuration = copy.deepcopy(self.configuration) configuration.constraints['max_airmass'] = airmass req = Request(configurations=[configuration], windows=dt_windows, request_id=1, duration=10) return req
def test_is_empty_has_windows_empty_on_one_resource(self): window_dict = { 'start': "2013-03-01T00:00:00Z", 'end': "2013-03-01T00:30:00Z", } w = Window(window_dict=window_dict, resource=self.t1['name']) w2 = Window(window_dict=window_dict, resource=self.t2['name']) windows = Windows() windows.append(w) windows.append(w2) windows.windows_for_resource[self.t2['name']] = [] assert_equal(windows.has_windows(), True) assert_equal(windows.size(), 1)
def test_make_target_intervals(self): window_dict = {'start': self.start, 'end': self.end} resource_name = '1m0a.doma.bpl' resource = self.tels[resource_name] window = Window(window_dict, resource['name']) dt_windows = Windows() dt_windows.append(window) req = Request(configurations=[self.configuration], windows=dt_windows, request_id='1') visibilities = construct_visibilities(self.tels, self.start, self.end) intervals_for_resource = self.make_rise_set_intervals( req, visibilities) compute_request_availability(req, intervals_for_resource, {}) received = req_windows_to_kernel_intervals( req.windows.windows_for_resource) date_format = '%Y-%m-%d %H:%M:%S.%f' rise_set_dark_intervals = (datetime.strptime( '2011-11-01 02:02:43.257196', date_format), datetime.strptime( '2011-11-01 07:52:00.564199', date_format), datetime.strptime( '2011-11-02 02:01:50.423880', date_format), datetime.strptime( '2011-11-02 07:48:04.692316', date_format)) # Verify we get the intervals we expect for resource_name, received_intervals in received.items(): for i, received_tp in enumerate(received_intervals.toDictList()): assert_equal(received_tp['time'], rise_set_dark_intervals[i])
def test_drop_empty_requests(self): request_id = 5 r = Request( configurations=None, windows=Windows(), request_id=request_id ) rg1 = RequestGroup( operator='single', requests=[r], proposal=None, expires=None, rg_id=1, is_staff=False, name=None, ipp_value=1.0, observation_type='NORMAL', submitter='', ) received = drop_empty_requests([rg1]) assert_equal(received, [5])
def test_visibility_intervals_at_low_horizon_are_allowed_by_hour_angle( self): window_dict = { 'start': datetime(2013, 3, 22, 0, 0, 0), 'end': datetime(2013, 3, 23, 0, 0, 0), } tel_name = '1m0a.doma.coj' tel = dict(name=tel_name, tel_class='1m0', latitude=-31.273, longitude=149.070593, horizon=15, ha_limit_neg=-4.6, ha_limit_pos=4.6, zenith_blind_spot=0.0) tels = { tel_name: tel, } target = ICRSTarget( # RA 15:41:25.91 ra=235.357958333, dec=-60.0, ) window = Window(window_dict, tel['name']) dt_windows = Windows() dt_windows.append(window) configuration = copy.deepcopy(self.configuration) configuration.target = target req = Request( configurations=[configuration], windows=dt_windows, request_id='1', duration=10, ) sem_start = datetime(2013, 3, 1, 0, 0, 0) sem_end = datetime(2013, 3, 31, 0, 0, 0) visibilities = construct_visibilities(tels, sem_start, sem_end) intervals_for_resource = self.make_rise_set_intervals( req, visibilities) compute_request_availability(req, intervals_for_resource, {}) received = req_windows_to_kernel_intervals( req.windows.windows_for_resource) # Hour angle not violated independently confirmed by hand-cranking through SLALIB expected_tps = [ { 'type': 'start', 'time': datetime(2013, 3, 22, 13, 9, 28, 988253) }, { 'type': 'end', 'time': datetime(2013, 3, 22, 19, 16, 27, 292072) }, ] for received_tp, expected_tp in zip(received[tel_name].toDictList(), expected_tps): assert_equal(received_tp['type'], expected_tp['type']) assert_equal(received_tp['time'], expected_tp['time'])
def test_has_windows_no_windows(self): windows = Windows() assert_equal(windows.has_windows(), False)
def _build_request_group(self, base_priority=1.0, ipp_value=1.0): operator = 'single' proposal = Proposal(id='LCOSchedulerTest', pi='Eric Saunders', tag='admin', tac_priority=base_priority) instrument_config = dict(exposure_count=1, bin_x=2, bin_y=2, exposure_time=20, extra_params={}, optical_elements={'filter': 'BSSL-UX-020'}) guiding_config = dict(mode='ON', optional=True, optical_elements={}, extra_params={}, exposure_time=10) acquisition_config = dict(mode='OFF', extra_params={}) constraints = {'max_airmass': None, 'min_lunar_distance': 0.0} configuration1 = Configuration( dict(id=5, target=None, type='expose', instrument_type='1M0-SCICAM-SBIG', priority=1, instrument_configs=[instrument_config], acquisition_config=acquisition_config, guiding_config=guiding_config, extra_params={}, constraints=constraints)) telescope = dict( name='maui', latitude=20.7069444444, longitude=-156.258055556, ) window_dict = { 'start': "2013-03-01T00:00:00Z", 'end': "2013-03-01T00:30:00Z", } w = Window(window_dict=window_dict, resource=telescope['name']) windows = Windows() windows.append(w) r = Request(configurations=[configuration1], windows=windows, request_id='0000000003', duration=10) rg = RequestGroup(operator=operator, requests=[r], proposal=proposal, expires=None, rg_id=4, is_staff=False, ipp_value=ipp_value, observation_type='NORMAL', name=None, submitter='Eric Saunders') return rg
def setup(self): self.target = ICRSTarget( name='deneb', ra=310.35795833333333, dec=45.280338888888885, epoch=2000, ) self.telescope = dict(name='1m0a.doma.ogg', latitude=20.7069444444, longitude=-156.258055556, tel_class='1m0', horizon=15, status='online', ha_limit_neg=-4.6, ha_limit_pos=4.6, zenith_blind_spot=0.0) self.telescopes = {'1m0a.doma.ogg': self.telescope} self.proposal = Proposal(id='LCOSchedulerTest', pi='Eric Saunders', tag='admin', tac_priority=1) self.instrument_config = dict(exposure_count=1, bin_x=2, bin_y=2, exposure_time=60 * 25, optical_elements={'filter': 'b'}) self.guiding_config = dict(mode='ON', optional=True, optical_elements={}, exposure_time=10) self.acquisition_config = dict(mode='OFF') self.constraints = {'max_airmass': None, 'min_lunar_distance': 0} self.configuration = Configuration( **dict(id=5, target=self.target, type='expose', instrument_type='1M0-SCICAM-SBIG', priority=1, instrument_configs=[self.instrument_config], acquisition_config=self.acquisition_config, guiding_config=self.guiding_config, constraints=self.constraints)) self.base_time = datetime(2016, 9, 14, 6, 0) resource_1 = '1m0a.doma.ogg' self.window_1 = Window( { 'start': self.base_time, 'end': self.base_time + timedelta(hours=0, minutes=30) }, resource_1) self.windows_1 = Windows() self.windows_1.append(self.window_1) resource_2 = '1m0a.doma.ogg' self.window_2 = Window( { 'start': self.base_time + timedelta(hours=0, minutes=30), 'end': self.base_time + timedelta(hours=1, minutes=0) }, resource_2) self.windows_2 = Windows() self.windows_2.append(self.window_2) self.resource_3 = '1m0a.doma.ogg' self.window_3 = Window( { 'start': self.base_time + timedelta(hours=1, minutes=0), 'end': self.base_time + timedelta(hours=1, minutes=30) }, self.resource_3) self.windows_3 = Windows() self.windows_3.append(self.window_3) self.request_1 = Request(configurations=[self.configuration], windows=self.windows_1, request_id=1, duration=1750) self.request_2 = Request(configurations=[self.configuration], windows=self.windows_2, request_id=2, duration=1750) self.request_3 = Request(configurations=[self.configuration], windows=self.windows_2, request_id=3, duration=1750) self.request_4 = Request(configurations=[self.configuration], windows=self.windows_3, request_id=4, duration=1750) self.request_5 = Request(configurations=[self.configuration], windows=self.windows_3, request_id=5, duration=1750) self.and_request_group_1 = RequestGroup( operator='and', requests=[self.request_1, self.request_2], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=1, is_staff=False, observation_type='NORMAL', ipp_value=1.0, name='ur 1', submitter='') self.and_request_group_2 = RequestGroup( operator='and', requests=[self.request_3, self.request_4], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=2, is_staff=False, observation_type='NORMAL', ipp_value=1.0, name='ur 2', submitter='') self.many_request_group_1 = RequestGroup( operator='many', requests=[self.request_1, self.request_2], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=3, is_staff=False, observation_type='NORMAL', ipp_value=1.5, name='ur 3', submitter='') self.many_request_group_2 = RequestGroup( operator='many', requests=[self.request_3, self.request_4], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=4, is_staff=False, observation_type='NORMAL', ipp_value=1.5, name='ur 4', submitter='') self.rr_request_group_1 = RequestGroup( operator='many', requests=[self.request_5], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=5, is_staff=False, observation_type='RAPID_RESPONSE', ipp_value=1.5, name='ur 5', submitter='') self.rr_request_group_2 = RequestGroup( operator='many', requests=[self.request_1, self.request_3], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=6, is_staff=False, observation_type='RAPID_RESPONSE', ipp_value=1.5, name='ur 6', submitter='')
class TestIntegration(object): '''Unit tests for the adaptive scheduler Request object.''' def setup(self): self.target = ICRSTarget( name='deneb', ra=310.35795833333333, dec=45.280338888888885, epoch=2000, ) self.telescope = dict(name='1m0a.doma.ogg', latitude=20.7069444444, longitude=-156.258055556, tel_class='1m0', horizon=15, status='online', ha_limit_neg=-4.6, ha_limit_pos=4.6, zenith_blind_spot=0.0) self.telescopes = {'1m0a.doma.ogg': self.telescope} self.proposal = Proposal(id='LCOSchedulerTest', pi='Eric Saunders', tag='admin', tac_priority=1) self.instrument_config = dict(exposure_count=1, bin_x=2, bin_y=2, exposure_time=60 * 25, optical_elements={'filter': 'b'}) self.guiding_config = dict(mode='ON', optional=True, optical_elements={}, exposure_time=10) self.acquisition_config = dict(mode='OFF') self.constraints = {'max_airmass': None, 'min_lunar_distance': 0} self.configuration = Configuration( **dict(id=5, target=self.target, type='expose', instrument_type='1M0-SCICAM-SBIG', priority=1, instrument_configs=[self.instrument_config], acquisition_config=self.acquisition_config, guiding_config=self.guiding_config, constraints=self.constraints)) self.base_time = datetime(2016, 9, 14, 6, 0) resource_1 = '1m0a.doma.ogg' self.window_1 = Window( { 'start': self.base_time, 'end': self.base_time + timedelta(hours=0, minutes=30) }, resource_1) self.windows_1 = Windows() self.windows_1.append(self.window_1) resource_2 = '1m0a.doma.ogg' self.window_2 = Window( { 'start': self.base_time + timedelta(hours=0, minutes=30), 'end': self.base_time + timedelta(hours=1, minutes=0) }, resource_2) self.windows_2 = Windows() self.windows_2.append(self.window_2) self.resource_3 = '1m0a.doma.ogg' self.window_3 = Window( { 'start': self.base_time + timedelta(hours=1, minutes=0), 'end': self.base_time + timedelta(hours=1, minutes=30) }, self.resource_3) self.windows_3 = Windows() self.windows_3.append(self.window_3) self.request_1 = Request(configurations=[self.configuration], windows=self.windows_1, request_id=1, duration=1750) self.request_2 = Request(configurations=[self.configuration], windows=self.windows_2, request_id=2, duration=1750) self.request_3 = Request(configurations=[self.configuration], windows=self.windows_2, request_id=3, duration=1750) self.request_4 = Request(configurations=[self.configuration], windows=self.windows_3, request_id=4, duration=1750) self.request_5 = Request(configurations=[self.configuration], windows=self.windows_3, request_id=5, duration=1750) self.and_request_group_1 = RequestGroup( operator='and', requests=[self.request_1, self.request_2], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=1, is_staff=False, observation_type='NORMAL', ipp_value=1.0, name='ur 1', submitter='') self.and_request_group_2 = RequestGroup( operator='and', requests=[self.request_3, self.request_4], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=2, is_staff=False, observation_type='NORMAL', ipp_value=1.0, name='ur 2', submitter='') self.many_request_group_1 = RequestGroup( operator='many', requests=[self.request_1, self.request_2], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=3, is_staff=False, observation_type='NORMAL', ipp_value=1.5, name='ur 3', submitter='') self.many_request_group_2 = RequestGroup( operator='many', requests=[self.request_3, self.request_4], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=4, is_staff=False, observation_type='NORMAL', ipp_value=1.5, name='ur 4', submitter='') self.rr_request_group_1 = RequestGroup( operator='many', requests=[self.request_5], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=5, is_staff=False, observation_type='RAPID_RESPONSE', ipp_value=1.5, name='ur 5', submitter='') self.rr_request_group_2 = RequestGroup( operator='many', requests=[self.request_1, self.request_3], proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=6, is_staff=False, observation_type='RAPID_RESPONSE', ipp_value=1.5, name='ur 6', submitter='') def _schedule_requests(self, rr_rg_list, normal_rg_list, scheduler_time, rr_loop=False, block_schedule_by_resource=None, running_request_groups=None, rapid_response_ids=None, semester_details=None): if block_schedule_by_resource is None: block_schedule_by_resource = {} if running_request_groups is None: running_request_groups = [] if rapid_response_ids is None: rapid_response_ids = [] if semester_details is None: semester_details = {} sched_params = SchedulerParameters(run_once=True, dry_run=True, timelimit_seconds=30) event_bus_mock = Mock() scheduler = LCOGTNetworkScheduler(FullScheduler_ortoolkit, sched_params, event_bus_mock, self.telescopes) network_interface_mock = Mock() network_interface_mock.cancel = Mock(return_value=0) network_interface_mock.save = Mock(return_value=0) network_interface_mock.abort = Mock(return_value=0) network_interface_mock.get_current_events = Mock(return_value={}) mock_input_factory = create_scheduler_input_factory( rr_rg_list, normal_rg_list, block_schedule_by_resource, running_request_groups, rapid_response_ids) if rr_loop: scheduler_input = mock_input_factory.create_rr_scheduling_input() else: scheduler_input = mock_input_factory.create_normal_scheduling_input( ) scheduler_input.scheduler_now = scheduler_time scheduler_input.estimated_scheduler_end = scheduler_time + timedelta( minutes=15) if not semester_details: semester_details = { 'id': '2015A', 'start': scheduler_time - timedelta(days=150), 'end': scheduler_time + timedelta(days=150) } result = scheduler.run_scheduler(scheduler_input, scheduler_time + timedelta(minutes=15), semester_details, preemption_enabled=rr_loop) return result def test_changing_semester_details_clears_visibility_cache(self): scheduler_time = self.base_time - timedelta(hours=10) sched_params = SchedulerParameters(run_once=True, dry_run=True, timelimit_seconds=30) event_bus_mock = Mock() scheduler = LCOGTNetworkScheduler(FullScheduler_ortoolkit, sched_params, event_bus_mock, self.telescopes) network_interface_mock = Mock() network_interface_mock.cancel = Mock(return_value=0) network_interface_mock.save = Mock(return_value=0) network_interface_mock.abort = Mock(return_value=0) network_interface_mock.get_current_events = Mock(return_value={}) normal_ur_list = [self.and_request_group_1, self.and_request_group_2] mock_input_factory = create_scheduler_input_factory([], normal_ur_list, {}, [], []) scheduler_input = mock_input_factory.create_normal_scheduling_input() scheduler_input.scheduler_now = scheduler_time scheduler_input.estimated_scheduler_end = scheduler_time + timedelta( minutes=15) semester_details = { 'id': '2015A', 'start': scheduler_time - timedelta(days=150), 'end': scheduler_time + timedelta(days=150) } scheduler.run_scheduler(scheduler_input, scheduler_time + timedelta(minutes=15), semester_details, preemption_enabled=False) assert scheduler.visibility_cache != {} saved_visibility_cache = scheduler.visibility_cache # Now run again with a different semester to clear visibility cache semester_details['start'] = scheduler_time - timedelta(days=149) scheduler.run_scheduler(scheduler_input, scheduler_time + timedelta(minutes=15), semester_details, preemption_enabled=False) assert scheduler.visibility_cache != {} assert scheduler.visibility_cache != saved_visibility_cache def test_competing_and_requests(self): result = self._schedule_requests( [], [self.and_request_group_1, self.and_request_group_2], self.base_time - timedelta(hours=10)) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # assert that either user request 1 or user request 2 were scheduled in full, with the other not being scheduled if 1 in scheduled_rgs: # check that ur 1s requests are scheduled assert 1 in scheduled_rgs[1] assert 2 in scheduled_rgs[1] # and check that ur 2 is not scheduled assert 2 not in scheduled_rgs else: assert 4 in scheduled_rgs[2] assert 3 in scheduled_rgs[2] assert 1 not in scheduled_rgs def test_competing_many_requests(self): result = self._schedule_requests( [], [self.many_request_group_1, self.many_request_group_2], self.base_time - timedelta(hours=10)) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # assert that user request 3 request 1 and user request 4 request 4 were scheduled , # along with one of either 3-2 or 4-3. assert 3 in scheduled_rgs assert 4 in scheduled_rgs assert 1 in scheduled_rgs[3] assert 4 in scheduled_rgs[4] if 2 in scheduled_rgs[3]: assert 3 not in scheduled_rgs[4] else: assert 2 not in scheduled_rgs[3] def test_competing_many_and_requests(self): normal_request_list = [ self.and_request_group_1, self.many_request_group_2 ] result = self._schedule_requests([], normal_request_list, self.base_time - timedelta(hours=10)) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # assert the and request was taken in full, and the remaining many ur 4 request 2 was scheduled assert 1 in scheduled_rgs assert 1 in scheduled_rgs[1] assert 2 in scheduled_rgs[1] assert 4 in scheduled_rgs # the second request from the many was scheduled but the first was not assert 4 in scheduled_rgs[4] assert 3 not in scheduled_rgs[4] def test_large_and_requests(self): days_out = 0 # build up a request a day for 100 days out new_time = datetime(2016, 10, 3, 5, 0) request_list = [] while days_out < 80: resource = '1m0a.doma.ogg' window = Window( { 'start': new_time + timedelta(days=days_out), 'end': new_time + timedelta(days=days_out, hours=0, minutes=30) }, resource) windows = Windows() windows.append(window) request = Request(configurations=[self.configuration], windows=windows, request_id=int("11{}".format(days_out).rjust( 10, '0')), duration=1750) request_list.append(request) days_out += 1 request_group = RequestGroup(operator='and', requests=request_list, proposal=self.proposal, expires=datetime(2050, 1, 1), rg_id=100, is_staff=False, ipp_value=1.0, name='large ur', submitter='', observation_type='NORMAL') normal_request_list = [ request_group, ] result = self._schedule_requests([], normal_request_list, new_time - timedelta(hours=10)) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # assert that none of the and is scheduled (since it has an unschedulable request in it) # assert that both of the manys are scheduled assert 100 in scheduled_rgs for req in request_list: # assert each child request is in the schedule (scheduler schedules past horizon for ands) assert req.id in scheduled_rgs[100] def test_normal_requests_dont_schedule_over_rr(self): ''' Verifies that a normal request will not schedule over a just scheduled RR request ''' rr_schedule = { self.resource_3: { 'all': [ (self.base_time + timedelta(hours=1, minutes=0), self.base_time + timedelta(hours=1, minutes=25)), ] } } result = self._schedule_requests( [ self.rr_request_group_1, ], [ self.many_request_group_2, ], self.base_time - timedelta(hours=10), rr_loop=False, block_schedule_by_resource=rr_schedule) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure request 3 could be scheduled, but request 4 could not because it overlapped with the scheduled RR assert_true(4 in scheduled_rgs) assert_true(4 not in scheduled_rgs[4]) assert_true(3 in scheduled_rgs[4]) def test_rr_requests_dont_schedule_over_running_rr(self): ''' Verifies that a RR will not preempt a currently running RR if it overlaps with its window completely ''' rapid_response_id = 99 running_request_group = create_running_request_group( request_group_id=rapid_response_id, request_id=99, resource=self.resource_3, start=self.base_time, end=self.base_time + timedelta(hours=2)) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], self.base_time - timedelta(hours=10), rr_loop=True, block_schedule_by_resource={}, running_request_groups=[ running_request_group, ], rapid_response_ids=[ rapid_response_id, ]) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure no RR was scheduled because the running request group was over it's time assert_false(5 in scheduled_rgs) assert_equal(scheduled_rgs, {}) def test_rr_requests_do_schedule_over_running_normal(self): ''' Verifies that a RR will preempt a currently running normal request and be scheduled over it at its earliest time possible ''' rapid_response_id = 777 running_request_group = create_running_request_group( request_group_id=99, request_id=99, resource=self.resource_3, start=self.base_time, end=self.base_time + timedelta(hours=2)) scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], scheduler_start, rr_loop=True, block_schedule_by_resource={}, running_request_groups=[ running_request_group, ], rapid_response_ids=[ rapid_response_id, ]) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure RR was scheduled at its first time even though it overlaps with the currently running normal request assert_true(5 in scheduled_rgs) assert_true(5 in scheduled_rgs[5]) semester_start = scheduler_start - timedelta(days=150) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[5][5], semester_start) assert_equal(dt_start, self.window_3.start) assert_equal(dt_end, self.window_3.start + timedelta(seconds=1750)) def test_rr_requests_schedule_after_running_rr(self): ''' Verifies that a RR will be scheduled after a currently running RR if it is able ''' rapid_response_id = 99 running_request_group = create_running_request_group( request_group_id=rapid_response_id, request_id=99, resource=self.resource_3, start=self.base_time, end=self.base_time + timedelta(hours=1, minutes=0, seconds=30)) scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], scheduler_start, rr_loop=True, block_schedule_by_resource={}, running_request_groups=[ running_request_group, ], rapid_response_ids=[ rapid_response_id, ]) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure RR was scheduled after the running RR since there was still time assert_true(5 in scheduled_rgs) assert_true(5 in scheduled_rgs[5]) semester_start = scheduler_start - timedelta(days=150) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[5][5], semester_start) assert_equal( dt_start, self.base_time + timedelta(hours=1, minutes=0, seconds=30)) assert_equal( dt_end, self.base_time + timedelta(hours=1, minutes=0, seconds=30) + timedelta(seconds=1750)) def test_normal_requests_dont_schedule_over_running_rr(self): ''' Verifies that a normal request will be blocked by a currently running RR ''' rr_request_group_id = 99 running_request_group = create_running_request_group( request_group_id=rr_request_group_id, request_id=99, resource=self.resource_3, start=self.base_time, end=self.base_time + timedelta(hours=2)) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], self.base_time - timedelta(hours=10), rr_loop=False, block_schedule_by_resource={}, running_request_groups=[ running_request_group, ], rapid_response_ids=[ rr_request_group_id, ]) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure request 3 could be scheduled, but request 4 could not because it overlapped with the scheduled RR assert_false(4 in scheduled_rgs) assert_false(3 in scheduled_rgs) assert_equal(scheduled_rgs, {}) def test_normal_requests_can_schedule_after_rr(self): ''' Verifies that a normal request will respect a previously scheduled RR whose time overlaps with it's window. Ensures that the normal request starts after the end of the RR. ''' rr_schedule = { self.resource_3: { 'all': [ (self.base_time + timedelta(hours=1, minutes=0), self.base_time + timedelta(hours=1, minutes=0, seconds=30)), ] } } scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests( [ self.rr_request_group_1, ], [ self.many_request_group_2, ], scheduler_start, rr_loop=False, block_schedule_by_resource=rr_schedule) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure both requests can get scheduled, but request 4 is after the RR reservation in its window assert_true(4 in scheduled_rgs) assert_true(4 in scheduled_rgs[4]) assert_true(3 in scheduled_rgs[4]) semester_start = scheduler_start - timedelta(days=150) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[4][4], semester_start) assert_equal( dt_start, self.base_time + timedelta(hours=1, minutes=0, seconds=30)) assert_equal( dt_end, self.base_time + timedelta(hours=1, minutes=0, seconds=30) + timedelta(seconds=1750)) def test_normal_requests_can_schedule_after_running_rr(self): ''' Verifies that a normal request will respect a already running RR whose time overlaps with it's window. Ensures that the normal request starts after the end of the RR. ''' rr_request_group_id = 99 running_request_group = create_running_request_group( request_group_id=rr_request_group_id, request_id=99, resource=self.resource_3, start=self.base_time, end=self.base_time + timedelta(hours=1, minutes=0, seconds=30)) scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], scheduler_start, rr_loop=False, block_schedule_by_resource={}, running_request_groups=[ running_request_group, ], rapid_response_ids=[ rr_request_group_id, ]) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() # Ensure request 4 is after the RR running request in its window, and request 3 is blocked by the running RR assert_true(4 in scheduled_rgs) assert_true(4 in scheduled_rgs[4]) assert_false(3 in scheduled_rgs[4]) semester_start = scheduler_start - timedelta(days=150) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[4][4], semester_start) assert_equal( dt_start, self.base_time + timedelta(hours=1, minutes=0, seconds=30)) assert_equal( dt_end, self.base_time + timedelta(hours=1, minutes=0, seconds=30) + timedelta(seconds=1750)) def test_one_rr_has_correct_cancel_date_list(self): ''' Schedules a single RR and verifies it's time appears in the cancellation date list on the resource ''' scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests([ self.rr_request_group_1, ], [ self.many_request_group_2, ], scheduler_start, rr_loop=True, block_schedule_by_resource={}) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() assert_true(5 in scheduled_rgs) assert_true(5 in scheduled_rgs[5]) semester_start = scheduler_start - timedelta(days=150) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[5][5], semester_start) scheduler_runner = SchedulerRunner(SchedulerParameters(dry_run=True), Mock(), Mock(), Mock(), Mock()) scheduler_runner.semester_details = { 'id': '2015A', 'start': semester_start, 'end': scheduler_start + timedelta(days=150) } cancel_date_list_by_resource = scheduler_runner._determine_schedule_cancelation_list_from_new_schedule( result.schedule) assert_true('1m0a.doma.ogg' in cancel_date_list_by_resource) assert_equal(len(cancel_date_list_by_resource['1m0a.doma.ogg']), 1) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][0][0], dt_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][0][1], dt_end) def test_multiple_rr_has_correct_cancel_date_list(self): ''' Schedules three nearly back to back RRs. Checks that each of their scheduled time appears in the date list for the resource they are scheduled in when getting dates to cancel. ''' scheduler_start = self.base_time - timedelta(hours=10) result = self._schedule_requests( [self.rr_request_group_2, self.rr_request_group_1], [ self.many_request_group_2, ], scheduler_start, rr_loop=True, block_schedule_by_resource={}) scheduled_rgs = result.get_scheduled_requests_by_request_group_id() assert_true(5 in scheduled_rgs) assert_true(5 in scheduled_rgs[5]) assert_true(6 in scheduled_rgs) assert_true(3 in scheduled_rgs[6]) assert_true(1 in scheduled_rgs[6]) semester_start = scheduler_start - timedelta(days=150) scheduler_runner = SchedulerRunner(SchedulerParameters(dry_run=True), Mock(), Mock(), Mock(), Mock()) scheduler_runner.semester_details = { 'id': '2015A', 'start': semester_start, 'end': scheduler_start + timedelta(days=150) } cancel_date_list_by_resource = scheduler_runner._determine_schedule_cancelation_list_from_new_schedule( result.schedule) assert_true('1m0a.doma.ogg' in cancel_date_list_by_resource) assert_equal(len(cancel_date_list_by_resource['1m0a.doma.ogg']), 3) assert_equal(len(cancel_date_list_by_resource['1m0a.doma.ogg']), 3) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[6][1], semester_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][0][0], dt_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][0][1], dt_end) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[6][3], semester_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][1][0], dt_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][1][1], dt_end) dt_start, dt_end = get_reservation_datetimes(scheduled_rgs[5][5], semester_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][2][0], dt_start) assert_equal(cancel_date_list_by_resource['1m0a.doma.ogg'][2][1], dt_end)