def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['site'] = forms.ChoiceField( choices=configdb.get_site_tuples()) self.fields['enclosure'] = forms.ChoiceField( choices=configdb.get_enclosure_tuples()) self.fields['telescope'] = forms.ChoiceField( choices=configdb.get_telescope_tuples())
class InstrumentsInformationFilter(django_filters.FilterSet): site = django_filters.MultipleChoiceFilter( choices=configdb.get_site_tuples(), label='Site code') enclosure = django_filters.MultipleChoiceFilter( choices=configdb.get_enclosure_tuples(), label='Enclosure code') telescope_class = django_filters.MultipleChoiceFilter( choices=configdb.get_telescope_class_tuples(), label='Telescope class') telescope = django_filters.MultipleChoiceFilter( choices=configdb.get_telescope_tuples(), label='Telescope code')
class PondBlockFilter(mixins.CustomIsoDateTimeFilterMixin, django_filters.FilterSet): site = django_filters.MultipleChoiceFilter( choices=configdb.get_site_tuples()) observatory = django_filters.MultipleChoiceFilter( choices=configdb.get_enclosure_tuples(), field_name='enclosure') telescope = django_filters.MultipleChoiceFilter( choices=configdb.get_telescope_tuples()) start_after = django_filters.IsoDateTimeFilter( field_name='start', lookup_expr='gte', label='Start after', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) start_before = django_filters.IsoDateTimeFilter( field_name='start', lookup_expr='lt', label='Start before', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) end_after = django_filters.IsoDateTimeFilter(field_name='end', lookup_expr='gte', label='End after') end_before = django_filters.IsoDateTimeFilter(field_name='end', lookup_expr='lt', label='End before') modified_after = django_filters.IsoDateTimeFilter(field_name='modified', lookup_expr='gte', label='Modified After') request_num = django_filters.CharFilter(field_name='request__id') tracking_num = django_filters.CharFilter( field_name='request__request_group__id') proposal = django_filters.CharFilter( field_name='request__request_group__proposal__id', distinct=True, lookup_expr='exact') instrument_class = django_filters.ChoiceFilter( choices=configdb.get_instrument_type_tuples(), field_name='configuration_statuses__configuration__instrument_type') canceled = django_filters.BooleanFilter(method='filter_canceled') order = django_filters.OrderingFilter(fields=('start', 'modified')) time_span = django_filters.DateRangeFilter(field_name='start') class Meta: model = Observation exclude = ['start', 'end', 'request', 'created', 'modified'] def filter_canceled(self, queryset, name, value): if not value: return queryset.exclude(state='CANCELED') else: return queryset
class LocationSerializer(serializers.ModelSerializer): site = serializers.ChoiceField(choices=configdb.get_site_tuples(), required=False) enclosure = serializers.ChoiceField( choices=configdb.get_enclosure_tuples(), required=False) telescope = serializers.ChoiceField( choices=configdb.get_telescope_tuples(), required=False) telescope_class = serializers.ChoiceField( choices=configdb.get_telescope_class_tuples(), required=True) class Meta: model = Location exclude = Location.SERIALIZER_EXCLUDE def validate(self, data): if 'enclosure' in data and 'site' not in data: raise serializers.ValidationError( _("Must specify a site with an enclosure.")) if 'telescope' in data and 'enclosure' not in data: raise serializers.ValidationError( _("Must specify an enclosure with a telescope.")) site_data_dict = { site['code']: site for site in configdb.get_site_data() } if 'site' in data: if data['site'] not in site_data_dict: msg = _('Site {} not valid. Valid choices: {}').format( data['site'], ', '.join(site_data_dict.keys())) raise serializers.ValidationError(msg) enc_set = site_data_dict[data['site']]['enclosure_set'] enc_dict = {enc['code']: enc for enc in enc_set} if 'enclosure' in data: if data['enclosure'] not in enc_dict: raise serializers.ValidationError( _(f'Enclosure {data["enclosure"]} not valid. Valid choices: {", ".join(enc_dict.keys())}' )) tel_set = enc_dict[data['enclosure']]['telescope_set'] tel_list = [tel['code'] for tel in tel_set] if 'telescope' in data and data['telescope'] not in tel_list: msg = _( 'Telescope {} not valid. Valid choices: {}').format( data['telescope'], ', '.join(tel_list)) raise serializers.ValidationError(msg) return data def to_representation(self, instance): """ This method is overridden to remove blank fields from serialized output. We could put this into a subclassed ModelSerializer if we want it to apply to all our Serializers. """ rep = super().to_representation(instance) return {key: val for key, val in rep.items() if val}
class ScheduleSerializer(serializers.ModelSerializer): """ Used to validate direct-submitted observations """ configuration_statuses = ConfigurationStatusSerializer(many=True, read_only=True) request = ObserveRequestSerializer() proposal = serializers.CharField(write_only=True) name = serializers.CharField(write_only=True) site = serializers.ChoiceField(choices=configdb.get_site_tuples()) enclosure = serializers.ChoiceField(choices=configdb.get_enclosure_tuples()) telescope = serializers.ChoiceField(choices=configdb.get_telescope_tuples()) state = serializers.ReadOnlyField() class Meta: model = Observation fields = ('site', 'enclosure', 'telescope', 'start', 'end', 'state', 'configuration_statuses', 'request', 'proposal', 'priority', 'name', 'id', 'modified') read_only_fields = ('modified', 'id', 'configuration_statuses') def validate_end(self, value): if value < timezone.now(): raise serializers.ValidationError(_("End time must be in the future")) return value def validate_proposal(self, value): try: proposal = Proposal.objects.get(id=value) except Proposal.DoesNotExist: raise serializers.ValidationError(_("Proposal {} does not exist".format(value))) if not proposal.direct_submission: raise serializers.ValidationError(_("Proposal {} is not allowed to submit observations directly".format( value ))) return value def validate(self, data): # Validate the observation times if data['end'] <= data['start']: raise serializers.ValidationError(_("End time must be after start time")) # Validate the site/obs/tel is a valid combination with the instrument class requested allowable_instruments = configdb.get_instruments_at_location( data['site'], data['enclosure'], data['telescope'] ) for configuration in data['request']['configurations']: if configuration['instrument_type'].lower() not in allowable_instruments['types']: raise serializers.ValidationError(_("instrument type {} is not available at {}.{}.{}".format( configuration['instrument_type'], data['site'], data['enclosure'], data['telescope'] ))) if not configuration.get('instrument_name', ''): instrument_names = configdb.get_instrument_names( configuration['instrument_type'], data['site'], data['enclosure'], data['telescope'] ) if len(instrument_names) > 1: raise serializers.ValidationError(_( 'There is more than one valid instrument on the specified telescope, please select from: {}' .format(instrument_names) )) else: configuration['instrument_name'] = instrument_names.pop() elif configuration['instrument_name'].lower() not in allowable_instruments['names']: raise serializers.ValidationError(_( '{} is not an available {} instrument on {}.{}.{}, available instruments are: {}'.format( configuration['instrument_name'], configuration['instrument_type'], data['site'], data['enclosure'], data['telescope'], allowable_instruments['names'] ) )) # Also check the guide and acquisition cameras are valid if specified # But only if the guide mode is set if ( configuration['guiding_config'].get('mode', GuidingConfig.OFF) != GuidingConfig.OFF or configuration['acquisition_config'].get('mode', AcquisitionConfig.OFF) != AcquisitionConfig.OFF ): if not configuration.get('guide_camera_name', ''): if ( 'extra_params' in configuration and 'self_guide' in configuration['extra_params'] and configuration['extra_params']['self_guide'] ): configuration['guide_camera_name'] = configuration['instrument_name'] else: configuration['guide_camera_name'] = configdb.get_guider_for_instrument_name( configuration['instrument_name'] ) if not configdb.is_valid_guider_for_instrument_name(configuration['instrument_name'], configuration['guide_camera_name']): raise serializers.ValidationError(_("Invalid guide camera {} for instrument {}".format( configuration['guide_camera_name'], configuration['instrument_name'] ))) else: configuration['guide_camera_name'] = '' # Add in the request group defaults for an observation data['observation_type'] = RequestGroup.DIRECT data['operator'] = 'SINGLE' data['ipp_value'] = 1.0 return data def create(self, validated_data): # separate out the observation and request_group fields OBS_FIELDS = ['site', 'enclosure', 'telescope', 'start', 'end', 'priority'] obs_fields = {} for field in OBS_FIELDS: if field in validated_data: obs_fields[field] = validated_data[field] del validated_data[field] # pull out the instrument_names to store later config_instrument_names = [] for configuration in validated_data['request']['configurations']: config_instrument_names.append((configuration['instrument_name'], configuration['guide_camera_name'])) del configuration['instrument_name'] del configuration['guide_camera_name'] with transaction.atomic(): rgs = ObserveRequestGroupSerializer(data=validated_data, context=self.context) rgs.is_valid(True) rg = rgs.save() observation = Observation.objects.create(request=rg.requests.first(), **obs_fields) for i, config in enumerate(rg.requests.first().configurations.all()): ConfigurationStatus.objects.create( configuration=config, observation=observation, instrument_name=config_instrument_names[i][0], guide_camera_name=config_instrument_names[i][1] ) return observation def to_representation(self, instance): data = super().to_representation(instance) # Add in the indirect fields from the requestgroup parent data['proposal'] = instance.request.request_group.proposal.id data['submitter'] = instance.request.request_group.submitter.username data['name'] = instance.request.request_group.name data['ipp_value'] = instance.request.request_group.ipp_value data['observation_type'] = instance.request.request_group.observation_type data['request_group_id'] = instance.request.request_group.id # Move the configuration statuses inline with their corresponding configuration section config_statuses = data.get('configuration_statuses', []) config_status_by_id = {cs['configuration']: cs for cs in config_statuses} for config in data['request']['configurations']: id = config['id'] if id in config_status_by_id: del config_status_by_id[id]['configuration'] config['configuration_status'] = config_status_by_id[id]['id'] del config_status_by_id[id]['id'] config.update(config_status_by_id[id]) del data['configuration_statuses'] return data
class ObservationFilter(mixins.CustomIsoDateTimeFilterMixin, django_filters.FilterSet): site = django_filters.MultipleChoiceFilter( choices=sorted(configdb.get_site_tuples())) enclosure = django_filters.MultipleChoiceFilter( choices=sorted(configdb.get_enclosure_tuples())) telescope = django_filters.MultipleChoiceFilter( choices=sorted(configdb.get_telescope_tuples())) time_span = django_filters.DateRangeFilter(field_name='start', label='Time Span') start_after = django_filters.IsoDateTimeFilter( field_name='start', lookup_expr='gte', label='Start After (Inclusive)', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) start_before = django_filters.IsoDateTimeFilter( field_name='start', lookup_expr='lt', label='Start Before', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) end_after = django_filters.IsoDateTimeFilter( field_name='end', lookup_expr='gte', label='End After (Inclusive)', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) end_before = django_filters.IsoDateTimeFilter( field_name='end', lookup_expr='lt', label='End Before', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) modified_after = django_filters.IsoDateTimeFilter( field_name='modified', lookup_expr='gte', label='Modified After (Inclusive)', widget=forms.TextInput(attrs={ 'class': 'input', 'type': 'date' })) request_id = django_filters.NumberFilter(field_name='request__id') request_group_id = django_filters.NumberFilter( field_name='request__request_group__id', label='Request Group ID') state = django_filters.MultipleChoiceFilter( choices=Observation.STATE_CHOICES, field_name='state') observation_type = django_filters.MultipleChoiceFilter( choices=RequestGroup.OBSERVATION_TYPES, field_name='request__request_group__observation_type', label='Observation Type') request_state = django_filters.MultipleChoiceFilter( choices=Request.STATE_CHOICES, field_name='request__state', label='Request State') proposal = django_filters.CharFilter( field_name='request__request_group__proposal__id', label='Proposal') instrument_type = django_filters.MultipleChoiceFilter( choices=sorted(configdb.get_instrument_type_tuples()), label='Instrument Type', field_name='configuration_statuses__configuration__instrument_type') configuration_type = django_filters.MultipleChoiceFilter( choices=sorted(configdb.get_configuration_type_tuples()), label='Configuration Type', field_name='configuration_statuses__configuration__type') ordering = django_filters.OrderingFilter( fields=['start', 'end', 'modified', 'created', 'state'], label='Observation ordering') class Meta: model = Observation exclude = ['start', 'end', 'request', 'created', 'modified']