def get_telescope_states_for_request(request_dict, is_staff=False): # TODO: update to support multiple instruments in a list instrument_type = request_dict['configurations'][0]['instrument_type'] site_intervals = {} only_schedulable = not (is_staff and ConfigDB.is_location_fully_set( request_dict.get('location', {}))) # Build up the list of telescopes and their rise set intervals for the target on this request site_data = configdb.get_sites_with_instrument_type_and_location( instrument_type=instrument_type, site_code=request_dict['location']['site'] if 'site' in request_dict['location'] else '', enclosure_code=request_dict['location']['enclosure'] if 'enclosure' in request_dict['location'] else '', telescope_code=request_dict['location']['telescope'] if 'telescope' in request_dict['location'] else '', only_schedulable=only_schedulable) for site, details in site_data.items(): if site not in site_intervals: site_intervals[site] = get_filtered_rise_set_intervals_by_site( request_dict, site=site, is_staff=is_staff).get(site, []) # If you have no sites, return the empty dict here if not site_intervals: return {} # Retrieve the telescope states for that set of sites min_window_time = min( [window['start'] for window in request_dict['windows']]) max_window_time = max( [window['end'] for window in request_dict['windows']]) telescope_states = TelescopeStates( start=min_window_time, end=max_window_time, sites=list(site_intervals.keys()), instrument_types=[instrument_type], location_dict=request_dict.get('location', {}), only_schedulable=only_schedulable).get() # Remove the empty intervals from the dictionary site_intervals = { site: intervals for site, intervals in site_intervals.items() if intervals } # Filter the telescope states list with the site intervals filtered_telescope_states = filter_telescope_states_by_intervals( telescope_states, site_intervals, min_window_time, max_window_time) return filtered_telescope_states
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', ''), only_schedulable) if not telescope_details: return intervals intervals_by_site = get_rise_set_intervals_by_site(request_dict) intervalsets_by_telescope = intervals_by_site_to_intervalsets_by_telescope( intervals_by_site, telescope_details.keys()) filtered_intervalsets_by_telescope = filter_out_downtime_from_intervalsets( intervalsets_by_telescope) filtered_intervals_by_site = intervalsets_by_telescope_to_intervals_by_site( filtered_intervalsets_by_telescope) return filtered_intervals_by_site
def get_airmasses_for_request_at_sites(request_dict, is_staff=False): # TODO: Change to work with multiple instrument types and multiple constraints and multiple targets data = {'airmass_data': {}} instrument_type = request_dict['configurations'][0]['instrument_type'] constraints = request_dict['configurations'][0]['constraints'] target = request_dict['configurations'][0]['target'] target_type = str(target.get('type', '')).upper() only_schedulable = not (is_staff and ConfigDB.is_location_fully_set(request_dict.get('location', {}))) if target_type in ['ICRS', 'ORBITAL_ELEMENTS'] and TARGET_TYPE_HELPER_MAP[target_type](target).is_valid(): site_data = configdb.get_sites_with_instrument_type_and_location( instrument_type=instrument_type, site_code=request_dict['location'].get('site'), enclosure_code=request_dict['location'].get('enclosure'), telescope_code=request_dict['location'].get('telescope'), only_schedulable=only_schedulable ) rs_target = get_rise_set_target(target) for site_id, site_details in site_data.items(): night_times = [] site_lat = Angle(degrees=site_details['latitude']) site_lon = Angle(degrees=site_details['longitude']) site_alt = site_details['altitude'] intervals = get_filtered_rise_set_intervals_by_site(request_dict, site_id, is_staff=is_staff).get(site_id, []) for interval in intervals: night_times.extend( [time for time in date_range_from_interval(interval[0], interval[1], dt=timedelta(minutes=10))]) if len(night_times) > 0: if site_id not in data: data['airmass_data'][site_id] = {} data['airmass_data'][site_id]['times'] = [time.strftime('%Y-%m-%dT%H:%M') for time in night_times] data['airmass_data'][site_id]['airmasses'] = calculate_airmass_at_times( night_times, rs_target, site_lat, site_lon, site_alt ) data['airmass_limit'] = constraints['max_airmass'] return data
def get_airmasses_for_request_at_sites(request_dict, is_staff=False): data = { 'airmass_data': {}, } instrument_type = request_dict['configurations'][0]['instrument_type'] constraints = request_dict['configurations'][0]['constraints'] target = request_dict['configurations'][0]['target'] target_type = str(target.get('type', '')).upper() only_schedulable = not (is_staff and ConfigDB.is_location_fully_set( request_dict.get('location', {}))) if target_type in [ 'ICRS', 'ORBITAL_ELEMENTS' ] and TARGET_TYPE_HELPER_MAP[target_type](target).is_valid(): site_data = configdb.get_sites_with_instrument_type_and_location( instrument_type=instrument_type, site_code=request_dict['location'].get('site'), enclosure_code=request_dict['location'].get('enclosure'), telescope_code=request_dict['location'].get('telescope'), only_schedulable=only_schedulable) rs_target = get_rise_set_target(target) for site_id, site_details in site_data.items(): night_times = [] site_lat = Angle(degrees=site_details['latitude']) site_lon = Angle(degrees=site_details['longitude']) site_alt = site_details['altitude'] intervals = get_filtered_rise_set_intervals_by_site( request_dict, site_id, is_staff=is_staff).get(site_id, []) for interval in intervals: night_times.extend([ time for time in date_range_from_interval( interval[0], interval[1], dt=timedelta(minutes=10)) ]) if len(night_times) > 0: if site_id not in data: data['airmass_data'][site_id] = { 'times': [ time.strftime('%Y-%m-%dT%H:%M') for time in night_times ], } # Need to average airmass values for set of unique targets in request unique_targets_constraints = set([ json.dumps((configuration['target'], configuration['constraints'])) for configuration in request_dict['configurations'] ]) unique_count = len(unique_targets_constraints) max_airmass = 0.0 for target_constraints in unique_targets_constraints: (target, constraints) = json.loads(target_constraints) rs_target = get_rise_set_target(target) airmasses = calculate_airmass_at_times( night_times, rs_target, site_lat, site_lon, site_alt) if 'airmasses' in data['airmass_data'][site_id]: for index, airmass_value in enumerate(airmasses): data['airmass_data'][site_id]['airmasses'][ index] += airmass_value else: data['airmass_data'][site_id]['airmasses'] = airmasses max_airmass += constraints['max_airmass'] # Now we need to divide out the number of unique constraints/targets data['airmass_limit'] = max_airmass / unique_count data['airmass_data'][site_id]['airmasses'] = [ val / unique_count for val in data['airmass_data'][site_id]['airmasses'] ] return data
def validate(self, data): is_staff = False only_schedulable = True request_context = self.context.get('request') if request_context: is_staff = request_context.user.is_staff only_schedulable = not (is_staff and ConfigDB.is_location_fully_set(data.get('location', {}))) # check if the instrument specified is allowed # TODO: Check if ALL instruments are available at a resource defined by location if 'location' in data: # Check if the location is fully specified, and if not then use only schedulable instruments valid_instruments = configdb.get_instrument_types(data.get('location', {}), only_schedulable=only_schedulable) for configuration in data['configurations']: if configuration['instrument_type'] not in valid_instruments: msg = _("Invalid instrument type '{}' at site={}, enc={}, tel={}. \n").format( configuration['instrument_type'], data.get('location', {}).get('site', 'Any'), data.get('location', {}).get('enclosure', 'Any'), data.get('location', {}).get('telescope', 'Any') ) msg += _("Valid instruments include: ") for inst_name in valid_instruments: msg += inst_name + ', ' msg += '.' if is_staff and not only_schedulable: msg += '\nStaff users must fully specify location to schedule on non-SCHEDULABLE instruments' raise serializers.ValidationError(msg) if 'acceptability_threshold' not in data: data['acceptability_threshold'] = max( [configdb.get_default_acceptability_threshold(configuration['instrument_type']) for configuration in data['configurations']] ) # check that the requests window has enough rise_set visible time to accomodate the requests duration if data.get('windows'): duration = get_request_duration(data) rise_set_intervals_by_site = get_filtered_rise_set_intervals_by_site(data, is_staff=is_staff) largest_interval = get_largest_interval(rise_set_intervals_by_site) for configuration in data['configurations']: if 'REPEAT' in configuration['type'].upper() and configuration.get('fill_window'): max_configuration_duration = largest_interval.total_seconds() - duration + configuration.get('repeat_duration', 0) - 1 configuration['repeat_duration'] = max_configuration_duration duration = get_request_duration(data) # delete the fill window attribute, it is only used for this validation try: del configuration['fill_window'] except KeyError: pass if largest_interval.total_seconds() <= 0: raise serializers.ValidationError( _( 'According to the constraints of the request, the target is never visible within the time ' 'window. Check that the target is in the nighttime sky. Consider modifying the time ' 'window or loosening the airmass or lunar separation constraints. If the target is ' 'non sidereal, double check that the provided elements are correct.' ) ) if largest_interval.total_seconds() <= duration: raise serializers.ValidationError( ( 'According to the constraints of the request, the target is visible for a maximum of {0:.2f} ' 'hours within the time window. This is less than the duration of your request {1:.2f} hours. ' 'Consider expanding the time window or loosening the airmass or lunar separation constraints.' ).format( largest_interval.total_seconds() / 3600.0, duration / 3600.0 ) ) return data