Skip to content

Commit

Permalink
Merge f95908a into f7bcf08
Browse files Browse the repository at this point in the history
  • Loading branch information
eheinrich committed Nov 4, 2019
2 parents f7bcf08 + f95908a commit b0afba8
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 31 deletions.
15 changes: 10 additions & 5 deletions observation_portal/common/test_data/configdb.json
Expand Up @@ -48,7 +48,8 @@
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
"SCRIPT",
"SKY_FLAT"
],
"default_acceptability_threshold": 90,
"pixels_x": 1000,
Expand Down Expand Up @@ -172,7 +173,8 @@
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
"SCRIPT",
"SKY_FLAT"
],
"default_acceptability_threshold": 90,
"pixels_x": 1000,
Expand Down Expand Up @@ -296,7 +298,8 @@
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
"SCRIPT",
"SKY_FLAT"
],
"default_acceptability_threshold": 90,
"pixels_x": 1000,
Expand Down Expand Up @@ -598,7 +601,8 @@
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
"SCRIPT",
"SKY_FLAT"
],
"default_acceptability_threshold": 100,
"pixels_x": 1000,
Expand Down Expand Up @@ -1008,7 +1012,8 @@
"EXPOSE",
"BIAS",
"DARK",
"SCRIPT"
"SCRIPT",
"SKY_FLAT"
],
"default_acceptability_threshold": 100,
"pixels_x": 1000,
Expand Down
11 changes: 4 additions & 7 deletions observation_portal/requestgroups/serializers.py
Expand Up @@ -228,6 +228,8 @@ class Meta:
def validate_instrument_configs(self, value):
if [instrument_config.get('fill_window', False) for instrument_config in value].count(True) > 1:
raise serializers.ValidationError(_('Only one instrument_config can have `fill_window` set'))
if len(set([instrument_config.get('rotator_mode', '') for instrument_config in value])) > 1:
raise serializers.ValidationError(_('Rotator modes within the same configuration must be the same'))
return value

def validate_instrument_type(self, value):
Expand All @@ -250,12 +252,8 @@ def validate(self, data):
default_modes = configdb.get_default_modes_by_type(instrument_type)
guiding_config = data['guiding_config']

# Validate the number of instrument configs if this is not a SOAR instrument
# TODO: remove this check once we support multiple instrument configs
if len(data['instrument_configs']) != 1 and 'SOAR' not in instrument_type.upper():
raise serializers.ValidationError(_(
'Currently only a single instrument_config is supported. This restriction will be lifted in the future.'
))
if len(data['instrument_configs']) > 1 and data['type'] in ['SCRIPT', 'SKY_FLAT']:
raise serializers.ValidationError(_(f'Multiple instrument configs are not allowed for type {data["type"]}'))

# Validate the guide mode
guide_validation_helper = ModeValidationHelper('guiding', instrument_type, default_modes, modes)
Expand Down Expand Up @@ -372,7 +370,6 @@ def validate(self, data):
else:
instrument_config['optical_elements'][oe_type] = available_elements[value.lower()]


# Also check that any optical element group in configdb is specified in the request unless we are a BIAS or
# DARK or SCRIPT type observation
observation_types_without_oe = ['BIAS', 'DARK', 'SCRIPT']
Expand Down
72 changes: 59 additions & 13 deletions observation_portal/requestgroups/test/test_api.py
Expand Up @@ -1573,7 +1573,6 @@ def test_configurations_automatically_have_priority_set(self):
for i, configuration in enumerate(rg['requests'][0]['configurations']):
self.assertEqual(configuration['priority'], i + 1)

@skip("Requires multiple instrument configs")
def test_fill_window_on_more_than_one_instrument_config_fails(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['instrument_configs'].append(
Expand All @@ -1595,7 +1594,6 @@ def test_fill_window_one_configuration_fills_the_window(self):
initial_exposure_count)
self.assertEqual(response.status_code, 201)

@skip("Requires multiple instrument configs")
def test_fill_window_two_instrument_configs_one_false_fills_the_window(self):
good_data = self.generic_payload.copy()
good_data['requests'][0]['configurations'][0]['instrument_configs'].append(
Expand All @@ -1610,7 +1608,6 @@ def test_fill_window_two_instrument_configs_one_false_fills_the_window(self):
initial_exposure_count)
self.assertEqual(response.status_code, 201)

@skip("Requires multiple instrument configs")
def test_fill_window_two_instrument_configs_one_blank_fills_the_window(self):
good_data = self.generic_payload.copy()
good_data['requests'][0]['configurations'][0]['instrument_configs'].append(
Expand All @@ -1623,7 +1620,6 @@ def test_fill_window_two_instrument_configs_one_blank_fills_the_window(self):
self.assertGreater(rg['requests'][0]['configurations'][0]['instrument_configs'][0]['exposure_count'], initial_exposure_count)
self.assertEqual(response.status_code, 201)

@skip("Requires multiple instrument configs")
def test_fill_window_two_instrument_configs_first_fills_the_window(self):
good_data = self.generic_payload.copy()
good_data['requests'][0]['configurations'][0]['instrument_configs'].append(
Expand Down Expand Up @@ -1689,6 +1685,65 @@ def test_fill_window_when_exposures_fit_exactly(self):
n_exposures_fit_exactly - 1
)

def test_multiple_instrument_configs_with_different_rotator_modes_fails(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['instrument_configs'].append(
self.extra_configuration['instrument_configs'][0].copy()
)
bad_data['requests'][0]['configurations'][0]['instrument_type'] = '2M0-FLOYDS-SCICAM'
bad_data['requests'][0]['location']['telescope_class'] = '2m0'
bad_data['requests'][0]['configurations'][0]['type'] = 'SPECTRUM'
del bad_data['requests'][0]['configurations'][0]['instrument_configs'][0]['optical_elements']['filter']
del bad_data['requests'][0]['configurations'][0]['instrument_configs'][1]['optical_elements']['filter']
bad_data['requests'][0]['configurations'][0]['instrument_configs'][0]['optical_elements']['slit'] = 'slit_1.6as'
bad_data['requests'][0]['configurations'][0]['instrument_configs'][1]['optical_elements']['slit'] = 'slit_1.6as'
bad_data['requests'][0]['configurations'][0]['instrument_configs'][0]['rotator_mode'] = 'SKY'
bad_data['requests'][0]['configurations'][0]['instrument_configs'][0]['extra_params'] = {}
bad_data['requests'][0]['configurations'][0]['instrument_configs'][0]['extra_params']['rotator_angle'] = '1'
bad_data['requests'][0]['configurations'][0]['instrument_configs'][1]['rotator_mode'] = 'VFLOAT'
response = self.client.post(reverse('api:request_groups-list'), data=bad_data)
self.assertEqual(response.status_code, 400)
self.assertIn('within the same configuration must be the same', str(response.content))

def test_multiple_instrument_configs_with_same_rotator_modes_succeeds(self):
good_data = self.generic_payload.copy()
good_data['requests'][0]['configurations'][0]['instrument_configs'].append(
self.extra_configuration['instrument_configs'][0].copy()
)
good_data['requests'][0]['configurations'][0]['instrument_type'] = '2M0-FLOYDS-SCICAM'
good_data['requests'][0]['location']['telescope_class'] = '2m0'
good_data['requests'][0]['configurations'][0]['type'] = 'SPECTRUM'
del good_data['requests'][0]['configurations'][0]['instrument_configs'][0]['optical_elements']['filter']
del good_data['requests'][0]['configurations'][0]['instrument_configs'][1]['optical_elements']['filter']
good_data['requests'][0]['configurations'][0]['instrument_configs'][0]['optical_elements']['slit'] = 'slit_1.6as'
good_data['requests'][0]['configurations'][0]['instrument_configs'][1]['optical_elements']['slit'] = 'slit_1.6as'
good_data['requests'][0]['configurations'][0]['instrument_configs'][0]['rotator_mode'] = 'VFLOAT'
good_data['requests'][0]['configurations'][0]['instrument_configs'][1]['rotator_mode'] = 'VFLOAT'
response = self.client.post(reverse('api:request_groups-list'), data=good_data)
self.assertEqual(response.status_code, 201)

def test_script_type_not_allowed_for_multiple_instrument_configs(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['instrument_configs'].append(
self.extra_configuration['instrument_configs'][0].copy()
)
bad_data['requests'][0]['configurations'][0]['type'] = 'SCRIPT'
bad_data['requests'][0]['configurations'][0]['extra_params'] = {}
bad_data['requests'][0]['configurations'][0]['extra_params']['script_name'] = 'my_cool_thing'
response = self.client.post(reverse('api:request_groups-list'), data=bad_data)
self.assertEqual(response.status_code, 400)
self.assertIn('are not allowed for type SCRIPT', str(response.content))

def test_skyflat_not_allowed_for_multiple_instrument_configs(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['instrument_configs'].append(
self.extra_configuration['instrument_configs'][0].copy()
)
bad_data['requests'][0]['configurations'][0]['type'] = 'SKY_FLAT'
response = self.client.post(reverse('api:request_groups-list'), data=bad_data)
self.assertEqual(response.status_code, 400)
self.assertIn('are not allowed for type SKY_FLAT', str(response.content))

def test_configuration_type_matches_instrument(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['type'] = 'SPECTRUM'
Expand Down Expand Up @@ -1780,15 +1835,6 @@ def test_empty_roi_rejected(self):
self.assertEqual(response.status_code, 400)
self.assertIn('Must submit at least one bound for a region of interest', str(response.content))

def test_multiple_instrument_configs_currently_rejected(self):
bad_data = self.generic_payload.copy()
bad_data['requests'][0]['configurations'][0]['instrument_configs'].append(
bad_data['requests'][0]['configurations'][0]['instrument_configs'][0].copy()
)
response = self.client.post(reverse('api:request_groups-list'), data=bad_data)
self.assertEqual(response.status_code, 400)
self.assertIn('Currently only a single instrument_config', str(response.content))

def test_multiple_same_targets_and_constraints_accepted(self):
good_data = self.generic_payload.copy()
good_data['requests'][0]['configurations'].append(
Expand Down
14 changes: 11 additions & 3 deletions static/js/components/instrumentconfig.vue
Expand Up @@ -6,13 +6,13 @@
:index="index"
:errors="errors"
:canremove="this.index > 0"
:cancopy="false"
:cancopy="true"
@remove="$emit('remove')"
@copy="$emit('copy')"
@show="show = $event"
>
<customalert
v-for="error in errors.non_field_errors"
v-for="error in topLevelErrors"
:key="error"
alertclass="danger"
:dismissible="false"
Expand Down Expand Up @@ -143,7 +143,12 @@
<script>
import _ from 'lodash';
import { collapseMixin, lampFlatDefaultExposureTime, arcDefaultExposureTime } from '../utils.js';
import {
collapseMixin,
lampFlatDefaultExposureTime,
arcDefaultExposureTime,
extractTopLevelErrors
} from '../utils.js';
import customfield from './util/customfield.vue';
import customselect from './util/customselect.vue';
import panel from './util/panel.vue';
Expand Down Expand Up @@ -185,6 +190,9 @@ export default {
}
},
computed: {
topLevelErrors: function() {
return extractTopLevelErrors(this.errors);
},
instrumentHasRotatorModes: function() {
return this.available_instruments[this.selectedinstrument] && 'rotator' in this.available_instruments[this.selectedinstrument].modes;
},
Expand Down
9 changes: 7 additions & 2 deletions static/js/components/window.vue
Expand Up @@ -12,7 +12,7 @@
@show="show = $event"
>
<customalert
v-for="error in errors.non_field_errors"
v-for="error in topLevelErrors"
:key="error"
alertclass="danger"
:dismissible="false"
Expand Down Expand Up @@ -107,7 +107,7 @@
import $ from 'jquery';
import _ from 'lodash';
import { collapseMixin } from '../utils.js';
import { collapseMixin, extractTopLevelErrors } from '../utils.js';
import panel from './util/panel.vue';
import customalert from './util/customalert.vue';
import customdatetime from './util/customdatetime.vue';
Expand Down Expand Up @@ -145,6 +145,11 @@
jitter: 12.0
};
},
computed: {
topLevelErrors: function() {
return extractTopLevelErrors(this.errors);
},
},
methods: {
update: function() {
this.$emit('windowupdate');
Expand Down
17 changes: 16 additions & 1 deletion static/js/utils.js
Expand Up @@ -230,6 +230,20 @@ function formatValue(value){
return value;
}

function extractTopLevelErrors(errors) {
let topLevelErrors = [];
if (_.isString(errors)) {
// The error will be a string if a validate_xxx method of the parent serializer
// returned an error, for example the validate_instrument_configs method on the
// ConfigurationSerializer. These should be displayed at the top of a section.
topLevelErrors = _.concat(topLevelErrors, [errors]);
}
if (errors.non_field_errors) {
topLevelErrors = _.concat(topLevelErrors, errors.non_field_errors);
}
return topLevelErrors;
}

function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
Expand Down Expand Up @@ -345,5 +359,6 @@ export {
semesterStart, semesterEnd, sexagesimalRaToDecimal, sexagesimalDecToDecimal, QueryString, formatJson, formatValue,
formatDate, formatField, datetimeFormat, collapseMixin, siteToColor, siteCodeToName, arcDefaultExposureTime,
lampFlatDefaultExposureTime, observatoryCodeToNumber, telescopeCodeToName, colorPalette, julianToModifiedJulian,
getFieldDescription, decimalRaToSexigesimal, decimalDecToSexigesimal, tooltipConfig, addCsrfProtection
getFieldDescription, decimalRaToSexigesimal, decimalDecToSexigesimal, tooltipConfig, addCsrfProtection,
extractTopLevelErrors
};

0 comments on commit b0afba8

Please sign in to comment.