diff --git a/observation_portal/common/test_state_changes.py b/observation_portal/common/test_state_changes.py index 161fe23a..43588b0b 100644 --- a/observation_portal/common/test_state_changes.py +++ b/observation_portal/common/test_state_changes.py @@ -703,6 +703,68 @@ def test_request_state_configuration_status_failed_2(self): self.assertFalse(state_changed) self.assertEqual(self.request.state, 'PENDING') + def test_request_state_repeat_configuration_failed_and_threshold_failed(self): + with disconnect_signal(post_save, cb_configurationstatus_post_save, ConfigurationStatus): + self.request.state = 'PENDING' + self.request.save() + repeat_configuration = self.request.configurations.first() + repeat_configuration.type = 'REPEAT_EXPOSE' + repeat_configuration.repeat_duration = 2000 + repeat_configuration.save() + + observation = dmixer.blend( + Observation, request=self.request, start=self.now - timedelta(minutes=30), + end=self.now - timedelta(minutes=20) + ) + for configuration in self.request.configurations.all(): + if configuration.id == repeat_configuration.id: + cs = dmixer.blend( + ConfigurationStatus, observation=observation, configuration=configuration, state='FAILED', + ) + dmixer.blend(Summary, configuration_status=cs, time_completed=1000, start=observation.start, + end=observation.start + timedelta(seconds=1000)) + else: + cs = dmixer.blend( + ConfigurationStatus, observation=observation, configuration=configuration, state='FAILED', + ) + dmixer.blend(Summary, configuration_status=cs, time_completed=0) + state_changed = update_request_state(self.request, observation.configuration_statuses.all(), False) + self.request.refresh_from_db() + self.assertFalse(state_changed) + self.assertEqual(self.request.state, 'PENDING') + + def test_request_state_repeat_configuration_failed_but_threshold_reached(self): + with disconnect_signal(post_save, cb_configurationstatus_post_save, ConfigurationStatus): + self.request.state = 'PENDING' + self.request.acceptability_threshold = 90 + self.request.save() + repeat_configuration = self.request.configurations.last() + repeat_configuration.type = 'REPEAT_EXPOSE' + repeat_configuration.repeat_duration = 2000 + repeat_configuration.save() + + observation = dmixer.blend( + Observation, request=self.request, start=self.now - timedelta(minutes=30), + end=self.now - timedelta(minutes=20) + ) + repeat_starts = observation.start + timedelta(seconds=300) + for configuration in self.request.configurations.all(): + if configuration.id == repeat_configuration.id: + cs = dmixer.blend( + ConfigurationStatus, observation=observation, configuration=configuration, state='FAILED', + ) + dmixer.blend(Summary, configuration_status=cs, time_completed=1999, + start=repeat_starts, end=repeat_starts + timedelta(seconds=1999)) + else: + cs = dmixer.blend( + ConfigurationStatus, observation=observation, configuration=configuration, state='COMPLETED', + ) + dmixer.blend(Summary, configuration_status=cs, time_completed=100) + state_changed = update_request_state(self.request, observation.configuration_statuses.all(), False) + self.request.refresh_from_db() + self.assertTrue(state_changed) + self.assertEqual(self.request.state, 'COMPLETED') + class TestAggregateRequestStates(TestCase): def test_many_all_complete(self): diff --git a/observation_portal/requestgroups/request_utils.py b/observation_portal/requestgroups/request_utils.py index 2cf503f2..f6678873 100644 --- a/observation_portal/requestgroups/request_utils.py +++ b/observation_portal/requestgroups/request_utils.py @@ -122,20 +122,30 @@ def get_airmasses_for_request_at_sites(request_dict, is_staff=False): def exposure_completion_percentage(configuration_statuses): - total_exposure_time = 0 - completed_exposure_time = 0 + total_time = 0 + completed_time = 0 for configuration_status in configuration_statuses: - if hasattr(configuration_status, 'summary'): - completed_exposure_time += configuration_status.summary.time_completed - configuration_exposure_time = 0 - for instrument_config in configuration_status.configuration.instrument_configs.all(): - configuration_exposure_time += instrument_config.exposure_count * instrument_config.exposure_time - total_exposure_time += configuration_exposure_time - - if float(total_exposure_time) == 0: + is_repeat_type = ( + 'REPEAT' in configuration_status.configuration.type and + configuration_status.configuration.repeat_duration is not None + ) + has_summary = hasattr(configuration_status, 'summary') + if is_repeat_type: + if has_summary: + completed_time += ( + configuration_status.summary.end - configuration_status.summary.start + ).total_seconds() + total_time += configuration_status.configuration.repeat_duration + else: + if has_summary: + completed_time += configuration_status.summary.time_completed + for instrument_config in configuration_status.configuration.instrument_configs.all(): + total_time += instrument_config.exposure_count * instrument_config.exposure_time + + if float(total_time) == 0: return 100.0 - return (completed_exposure_time / total_exposure_time) * 100.0 + return (completed_time / total_time) * 100.0 def return_paginated_results(collection, url):