Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 45 additions & 17 deletions tom_observations/cadences/retry_failed_observations.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from datetime import timedelta
from dateutil.parser import parse
import logging

from tom_observations.cadence import BaseCadenceForm, CadenceStrategy
from tom_observations.models import ObservationRecord
from tom_observations.models import ObservationRecord, DynamicCadence
from tom_observations.facility import get_service_class

logger = logging.getLogger(__name__)


class RetryFailedObservationsForm(BaseCadenceForm):
pass
Expand All @@ -23,35 +26,60 @@ class RetryFailedObservationsStrategy(CadenceStrategy):
form = RetryFailedObservationsForm

def run(self):
failed_observations = [obsr for obsr
in self.dynamic_cadence.observation_group.observation_records.all()
if obsr.failed]
new_observations = []
for obs in failed_observations:
observation_payload = obs.parameters
facility = get_service_class(obs.facility)()
start_keyword, end_keyword = facility.get_start_end_keywords()
records = self.dynamic_cadence.observation_group.observation_records.all().order_by('-created')
last_obs = records.first()

if not last_obs:
return

facility_class = get_service_class(last_obs.facility)
facility = facility_class()
start_keyword, end_keyword = facility.get_start_end_keywords()
facility.update_observation_status(last_obs.observation_id)
last_obs.refresh_from_db()

if not last_obs.terminal: #observation is still pending, do nothing
return

elif not last_obs.failed: #observation succeeded
self.dynamic_cadence.active = False
self.dynamic_cadence.save()
return 'COMPLETED'

else: #observation failed, submit a new one
observation_payload = last_obs.parameters.copy()

observation_payload = self.advance_window(
observation_payload, start_keyword=start_keyword, end_keyword=end_keyword
)
obs_type = obs.parameters.get('observation_type', None)
form = facility.get_form(obs_type)(data=observation_payload)
form.is_valid()
observation_ids = facility.submit_observation(form.observation_payload())

obs_type = observation_payload.get('observation_type')
form = facility.get_form(obs_type)(observation_payload)

if not form.is_valid():
logger.error(msg=f'Unable to submit next cadenced observation: {form.errors}')
raise Exception(f'Unable to submit next cadenced observation: {form.errors}')

observation_ids = facility.submit_observation(form.observation_payload())
new_observations = []

for observation_id in observation_ids:
# Create Observation record
record = ObservationRecord.objects.create(
target=obs.target,
target=last_obs.target,
facility=facility.name,
parameters=observation_payload,
observation_id=observation_id
)
self.dynamic_cadence.observation_group.observation_records.add(record)
self.dynamic_cadence.observation_group.save()
new_observations.append(record)

return new_observations
self.dynamic_cadence.observation_group.save()

for obsr in new_observations:
facility.update_observation_status(obsr.observation_id)
obsr.refresh_from_db()

return new_observations

def advance_window(self, observation_payload, start_keyword='start', end_keyword='end'):
cadence_frequency = self.dynamic_cadence.cadence_parameters.get('cadence_frequency')
Expand Down
4 changes: 4 additions & 0 deletions tom_observations/management/commands/runcadencestrategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ def handle(self, *args, **kwargs):
continue
if not new_observations:
logger.log(msg=f'No changes from dynamic cadence {cg}', level=logging.INFO)
elif new_observations == 'COMPLETED':
logger.log(msg=f'''Single observation obtained for {cg},
no new observation submitted.''',
level=logging.INFO)
else:
logger.log(msg=f'''Cadence update completed for dynamic cadence {cg},
{len(new_observations)} new observations created.''',
Expand Down
22 changes: 21 additions & 1 deletion tom_observations/tests/test_cadence.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ def setUp(self):
cadence_strategy='Test Strategy', cadence_parameters={'cadence_frequency': 72}, active=True,
observation_group=self.group)

def test_retry_when_failed_cadence(self, patch1, patch2, patch3, patch4):
@patch('tom_observations.facilities.lco.LCOFacility.get_observation_status', return_value={'state': 'CANCELED',
'scheduled_start': None, 'scheduled_end': None})
def test_retry_when_failed_cadence_failed_obs(self, patch1, patch2, patch3, patch4, mock_get_obs_status, mock_validate_obs):
mock_validate_obs.return_value = {}
num_records = self.group.observation_records.count()
observing_record = self.group.observation_records.first()
observing_record.status = 'CANCELED'
Expand All @@ -76,6 +79,23 @@ def test_retry_when_failed_cadence(self, patch1, patch2, patch3, patch4):
parse(observing_record.parameters['start']),
parse(new_records[0].parameters['start']) - timedelta(days=3)
)

@patch('tom_observations.facilities.lco.LCOFacility.get_observation_status', return_value={'state': 'CANCELED',
'scheduled_start': None, 'scheduled_end': None})
def test_retry_when_failed_cadence_successful_obs(self, patch1, patch2, patch3, patch4, mock_get_obs_status, mock_validate_obs):
mock_validate_obs.return_value = {}
observing_record = self.group.observation_records.first()
observing_record.status = 'COMPLETE'
observing_record.save()

strategy = RetryFailedObservationsStrategy(self.dynamic_cadence)
new_records = strategy.run()
self.group.refresh_from_db()
# Make sure the candence returned 'COMPLETED'
self.assertEqual(new_records, 'COMPLETED')
# Make sure the dynamic cadence was turned off
self.assertEqual(self.dynamic_cadence.active, False)


@patch('tom_observations.facilities.lco.LCOFacility.get_observation_status', return_value={'state': 'CANCELED',
'scheduled_start': None, 'scheduled_end': None})
Expand Down
Loading