Example #1
0
 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')
Example #3
0
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
Example #4
0
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']