Skip to content

Commit

Permalink
Merge a38e11d into 13c4cb3
Browse files Browse the repository at this point in the history
  • Loading branch information
jnation3406 committed Aug 15, 2019
2 parents 13c4cb3 + a38e11d commit 8c0593b
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 69 deletions.
44 changes: 32 additions & 12 deletions observation_portal/common/configdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def get_site_data(self):
return self._get_configdb_data('sites')

def get_sites_with_instrument_type_and_location(
self, instrument_type: str = '', site_code: str = '', enclosure_code: str = '', telescope_code: str = ''
self, instrument_type: str = '', site_code: str = '', enclosure_code: str = '', telescope_code: str = '',
only_schedulable: bool = True
) -> dict:
"""Get the location details for each site for which a resource exists.
Expand All @@ -75,11 +76,12 @@ def get_sites_with_instrument_type_and_location(
site_code: 3-letter site code
enclosure_code: 4-letter enclosure code
telescope_code: 4-letter telescope code
only_schedulable: whether to only include SCHEDULABLE instrumets or all non-DISABLED
Returns:
Site location details
"""
telescope_details = self.get_telescopes_with_instrument_type_and_location(
instrument_type, site_code, enclosure_code, telescope_code
instrument_type, site_code, enclosure_code, telescope_code, only_schedulable
)
site_details = {}
for code in telescope_details.keys():
Expand Down Expand Up @@ -174,7 +176,7 @@ def get_instruments_at_location(self, site_code, enclosure_code, telescope_code,
return {'names': instrument_names, 'types': instrument_types}

def get_telescopes_with_instrument_type_and_location(
self, instrument_type='', site_code='', enclosure_code='', telescope_code=''
self, instrument_type='', site_code='', enclosure_code='', telescope_code='', only_schedulable=True
):
site_data = self.get_site_data()
telescope_details = {}
Expand All @@ -186,7 +188,8 @@ def get_telescopes_with_instrument_type_and_location(
if not telescope_code or telescope_code == telescope['code']:
code = '.'.join([telescope['code'], enclosure['code'], site['code']])
for instrument in telescope['instrument_set']:
if self.is_schedulable(instrument):
if (self.is_schedulable(instrument) or
(not only_schedulable and self.is_active(instrument))):
camera_type = instrument['science_camera']['camera_type']['code']
if not instrument_type or instrument_type.upper() == camera_type.upper():
if code not in telescope_details:
Expand Down Expand Up @@ -242,23 +245,29 @@ def get_instruments(self, exclude_states=None):

return instruments

def get_instrument_types_per_telescope(self, only_schedulable: bool = False) -> dict:
def get_instrument_types_per_telescope(self, location: dict = {}, only_schedulable: bool = False) -> dict:
"""Get a set of available instrument types per telescope.
Parameters:
only_schedulable: Whether to only include schedulable telescopes
Returns:
Available instrument types
"""
exclude_states = ['DISABLED']
if only_schedulable:
exclude_states = ['DISABLED', 'ENABLED', 'MANUAL', 'COMMISSIONING', 'STANDBY']
telescope_instrument_types = {}
for instrument in self.get_instruments(exclude_states=exclude_states):
if instrument['telescope_key'] not in telescope_instrument_types:
telescope_instrument_types[instrument['telescope_key']] = []
instrument_type = instrument['science_camera']['camera_type']['code'].upper()
if instrument_type not in telescope_instrument_types[instrument['telescope_key']]:
telescope_instrument_types[instrument['telescope_key']].append(instrument_type)
split_string = instrument['__str__'].lower().split('.')
if (location.get('site', '').lower() in split_string[0]
and location.get('enclosure', '').lower() in split_string[1]
and location.get('telescope_class', '').lower() in split_string[2]
and location.get('telescope', '').lower() in split_string[2]):
if instrument['telescope_key'] not in telescope_instrument_types:
telescope_instrument_types[instrument['telescope_key']] = []
instrument_type = instrument['science_camera']['camera_type']['code'].upper()
if instrument_type not in telescope_instrument_types[instrument['telescope_key']]:
telescope_instrument_types[instrument['telescope_key']].append(instrument_type)
return telescope_instrument_types

def get_instrument_names(
Expand Down Expand Up @@ -312,6 +321,7 @@ def get_telescopes_per_instrument_type(self, instrument_type: str, only_schedula
Returns:
Telescope keys
"""
exclude_states = ['DISABLED']
if only_schedulable:
exclude_states = ['DISABLED', 'ENABLED', 'MANUAL', 'COMMISSIONING', 'STANDBY']
instrument_telescopes = set()
Expand Down Expand Up @@ -470,7 +480,7 @@ def get_instrument_type_telescope_class(self, instrument_type):
return instrument['__str__'].split('.')[2][0:3]
return instrument_type[0:3]

def get_active_instrument_types(self, location: dict) -> set:
def get_instrument_types(self, location: dict, only_schedulable: bool = False) -> set:
"""Get the available instrument_types.
Parameters:
Expand All @@ -479,7 +489,10 @@ def get_active_instrument_types(self, location: dict) -> set:
Available instrument_types (i.e. 1M0-SCICAM-SBIG, etc.)
"""
instrument_types = set()
for instrument in self.get_instruments(exclude_states=['DISABLED', 'ENABLED', 'MANUAL', 'COMMISSIONING', 'STANDBY']):
exclude_states = ['DISABLED']
if only_schedulable:
exclude_states = ['DISABLED', 'ENABLED', 'MANUAL', 'COMMISSIONING', 'STANDBY']
for instrument in self.get_instruments(exclude_states=exclude_states):
split_string = instrument['__str__'].lower().split('.')
if (location.get('site', '').lower() in split_string[0]
and location.get('enclosure', '').lower() in split_string[1]
Expand Down Expand Up @@ -591,5 +604,12 @@ def is_active(instrument: dict) -> bool:
def is_schedulable(instrument: dict) -> bool:
return instrument['state'] == 'SCHEDULABLE'

@staticmethod
def is_location_fully_set(location: dict) -> bool:
for constraint in ['telescope_class', 'telescope', 'enclosure', 'site']:
if constraint not in location or not location.get(constraint):
return False
return True


configdb = ConfigDB()
8 changes: 5 additions & 3 deletions observation_portal/common/rise_set_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rise_set.moving_objects import MovingViolation
from django.core.cache import cache

from observation_portal.common.configdb import configdb
from observation_portal.common.configdb import configdb, ConfigDB
from observation_portal.common.downtimedb import DowntimeDB
from observation_portal.requestgroups.target_helpers import TARGET_TYPE_HELPER_MAP

Expand Down Expand Up @@ -72,14 +72,16 @@ def get_rise_set_intervals_by_site(request: dict) -> dict:
return intervals_by_site


def get_filtered_rise_set_intervals_by_site(request_dict, site=''):
def get_filtered_rise_set_intervals_by_site(request_dict, site='', is_staff=False):
intervals = {}
site = site if site else request_dict['location'].get('site', '')
only_schedulable = not (is_staff and ConfigDB.is_location_fully_set(request_dict.get('location', {})))
telescope_details = configdb.get_telescopes_with_instrument_type_and_location(
request_dict['configurations'][0]['instrument_type'],
site,
request_dict['location'].get('enclosure', ''),
request_dict['location'].get('telescope', '')
request_dict['location'].get('telescope', ''),
only_schedulable
)
if not telescope_details:
return intervals
Expand Down
10 changes: 6 additions & 4 deletions observation_portal/common/telescope_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ class TelescopeStates(object):
('Enclosure Shutter Mode: ', 'ENCLOSURE_DISABLED')
])

def __init__(self, start, end, telescopes=None, sites=None, instrument_types=None):
def __init__(self, start, end, telescopes=None, sites=None, instrument_types=None, location_dict={}, only_schedulable=True):
try:
self.es = Elasticsearch([settings.ELASTICSEARCH_URL])
except LocationValueError:
logger.error('Could not find host. Make sure ELASTICSEARCH_URL is set.')
raise ImproperlyConfigured('ELASTICSEARCH_URL')

self.instrument_types = instrument_types
self.available_telescopes = self._get_available_telescopes()
self.only_schedulable = only_schedulable
self.available_telescopes = self._get_available_telescopes(location_dict)

sites = list({tk.site for tk in self.available_telescopes}) if not sites else sites
telescopes = list({tk.telescope for tk in self.available_telescopes if tk.site in sites}) \
Expand All @@ -53,8 +54,9 @@ def __init__(self, start, end, telescopes=None, sites=None, instrument_types=Non
self.end = end.replace(tzinfo=timezone.utc).replace(microsecond=0)
self.event_data = self._get_es_data(sites, telescopes)

def _get_available_telescopes(self):
telescope_to_instruments = configdb.get_instrument_types_per_telescope(only_schedulable=True)
def _get_available_telescopes(self, location_dict={}):
telescope_to_instruments = configdb.get_instrument_types_per_telescope(location=location_dict,
only_schedulable=self.only_schedulable)
if not self.instrument_types:
available_telescopes = telescope_to_instruments.keys()
else:
Expand Down
148 changes: 146 additions & 2 deletions observation_portal/common/test_data/configdb.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@
"__str__": "tst.doma.1m0a.xx06-xx07"
},
{
"state": "ENABLED",
"state": "MANUAL",
"code": "xx08",
"autoguider_camera": {
"code": "xx09",
Expand Down Expand Up @@ -404,7 +404,7 @@
"horizon": 15.0,
"ha_limit_pos": 4.6,
"ha_limit_neg": -4.6,
"zenith_blind_spot":0.0,
"zenith_blind_spot": 0.0,
"slew_rate": 0.0,
"minimum_slew_overhead": 2.0,
"maximum_slew_overhead": 2.0,
Expand Down Expand Up @@ -962,6 +962,150 @@
]
}
]
},
{
"code": "domc",
"telescope_set": [
{
"code": "1m0a",
"lat": -32.3805542,
"long": 20.8100352,
"horizon": 15.0,
"ha_limit_pos": 4.6,
"ha_limit_neg": -4.6,
"zenith_blind_spot": 0.0,
"slew_rate": 0.0,
"minimum_slew_overhead": 2.0,
"maximum_slew_overhead": 2.0,
"instrument_change_overhead": 30.0,
"instrument_set": [
{
"state": "MANUAL",
"code": "xx11",
"autoguider_camera": {
"code": "ef11",
"camera_type": {
"code": "1M0-SCICAM-AG",
"allow_self_guiding": true
}
},
"science_camera": {
"code": "xx11",
"camera_type": {
"code": "1M0-SCICAM-SBIG",
"name": "1M0-SCICAM-SBIG",
"allow_self_guiding": true,
"default_mode": {
"binning": 2,
"readout": 14.5
},
"configuration_types": [
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
],
"default_acceptability_threshold": 100,
"pixels_x": 1000,
"pixels_y": 1000,
"max_rois": 0,
"config_change_time": 0,
"acquire_processing_time": 0,
"acquire_exposure_time": 0,
"front_padding": 90,
"filter_change_time": 2,
"fixed_overhead_per_exposure": 1,
"mode_set": [
{
"binning": 1,
"readout": 35.0
},
{
"binning": 2,
"readout": 14.5
},
{
"binning": 3,
"readout": 11.5
}
],
"mode_types": [
{
"type": "readout",
"default": "1m0_sbig_2",
"modes": [
{
"code": "1m0_sbig_1",
"overhead": 35.0,
"params": {
"binning": 1
}
},
{
"code": "1m0_sbig_2",
"overhead": 14.5,
"params": {
"binning": 2
}
},
{
"code": "1m0_sbig_3",
"overhead": 11.5,
"params": {
"binning": 3
}
}
]
},
{
"type": "acquisition",
"default": "OFF",
"modes": [
{
"code": "OFF",
"overhead": 0.0,
"params": {}
}
]
},
{
"type": "guiding",
"default": "OFF",
"modes": [
{
"code": "OFF",
"overhead": 0.0,
"params": {}
},
{
"code": "ON",
"overhead": 0.0,
"params": {}
}
]
}
]
},
"filters": "air",
"optical_element_groups": [
{
"type": "filters",
"element_change_overhead": 2,
"optical_elements": [
{
"name": "Air",
"code": "air",
"schedulable": false
}
]
}
]
},
"__str__": "tst.domc.1m0a.xx11-xx11"
}
]
}
]
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion observation_portal/observations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def validate(self, data):

# Validate that the site, enclosure, telescope has the appropriate instrument
available_instruments = configdb.get_instruments_at_location(
data['site'], data['enclosure'], data['telescope'], only_schedulable=True
data['site'], data['enclosure'], data['telescope'], only_schedulable=False
)
for configuration in data['request'].configurations.all():
if configuration.instrument_type.lower() not in available_instruments['types']:
Expand Down
4 changes: 2 additions & 2 deletions observation_portal/requestgroups/cadence.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import timedelta


def expand_cadence_request(request_dict):
def expand_cadence_request(request_dict, is_staff=False):
'''
Takes in a valid cadence request (valid request with cadence block), and expands the request into a list of requests
with their windows determined by the cadence parameters. Only valid requests that pass rise-set are returned, with
Expand All @@ -26,7 +26,7 @@ def expand_cadence_request(request_dict):

# test the rise_set of this window
request_dict['windows'] = [{'start': window_start, 'end': window_end}]
intervals_by_site = get_filtered_rise_set_intervals_by_site(request_dict)
intervals_by_site = get_filtered_rise_set_intervals_by_site(request_dict, is_staff=is_staff)
largest_interval = get_largest_interval(intervals_by_site)
if largest_interval.total_seconds() >= request_duration and window_end > timezone.now():
# this cadence window passes rise_set and is in the future so add it to the list
Expand Down
5 changes: 3 additions & 2 deletions observation_portal/requestgroups/duration_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ def get_configuration_duration(configuration_dict):
return conf_duration


def get_request_duration_dict(request_dict):
def get_request_duration_dict(request_dict, is_staff=False):
req_durations = {'requests': []}
for req in request_dict:
req_info = {'duration': get_request_duration(req)}
conf_durations = [get_configuration_duration(conf) for conf in req['configurations']]
req_info['configurations'] = conf_durations
req_info['largest_interval'] = get_largest_interval(get_filtered_rise_set_intervals_by_site(req)).total_seconds()
rise_set_intervals = get_filtered_rise_set_intervals_by_site(req, is_staff=is_staff)
req_info['largest_interval'] = get_largest_interval(rise_set_intervals).total_seconds()
req_info['largest_interval'] -= (PER_CONFIGURATION_STARTUP_TIME + PER_CONFIGURATION_GAP)
req_durations['requests'].append(req_info)
req_durations['duration'] = sum([req['duration'] for req in req_durations['requests']])
Expand Down

0 comments on commit 8c0593b

Please sign in to comment.