• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

observatorycontrolsystem / observation-portal / 13686447536

05 Mar 2025 10:04PM UTC coverage: 96.553% (-0.003%) from 96.556%
13686447536

Pull #326

github

Jon
Merge branch 'fix/visibility_in_future' of github.com:observatorycontrolsystem/observation-portal into fix/visibility_in_future
Pull Request #326: Changed behavior of checking visibliity windows to ensure that only f…

47 of 49 new or added lines in 7 files covered. (95.92%)

19 existing lines in 5 files now uncovered.

34982 of 36231 relevant lines covered (96.55%)

2.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.55
/observation_portal/requestgroups/test/test_request_utils.py
1
from django.utils import timezone
3✔
2
from django.test import TestCase
3✔
3
from mixer.backend.django import mixer
3✔
4
from datetime import datetime
3✔
5
from copy import deepcopy
3✔
6
from unittest.mock import patch
3✔
7

8
from time_intervals.intervals import Intervals
3✔
9

10
from observation_portal.requestgroups.request_utils import (get_airmasses_for_request_at_sites,
3✔
11
                                                            get_telescope_states_for_request,
12
                                                            get_filtered_rise_set_intervals_by_site)
13
from observation_portal.common.rise_set_utils import get_largest_interval
3✔
14
from observation_portal.requestgroups.models import (Request, Configuration, Target, RequestGroup, Window, Location,
3✔
15
                                                     Constraints, InstrumentConfig, AcquisitionConfig, GuidingConfig)
16
from observation_portal.proposals.models import Proposal, TimeAllocation, Semester
3✔
17
from observation_portal.common.test_telescope_states import TelescopeStatesFakeInput
3✔
18
from observation_portal.common.test_helpers import SetTimeMixin
3✔
19

20

21
class BaseSetupRequest(SetTimeMixin, TestCase):
3✔
22
    def setUp(self):
3✔
23
        super().setUp()
3✔
24

25
        self.proposal = mixer.blend(Proposal)
3✔
26
        semester = mixer.blend(
3✔
27
            Semester, id='2016B', start=datetime(2016, 9, 1, tzinfo=timezone.utc),
28
            end=datetime(2016, 12, 31, tzinfo=timezone.utc)
29
        )
30
        self.time_allocation_1m0 = mixer.blend(
3✔
31
            TimeAllocation, proposal=self.proposal, semester=semester, std_allocation=100.0, std_time_used=0.0,
32
            rr_allocation=10, rr_time_used=0.0, ipp_limit=10.0, ipp_time_available=5.0, tc_time_available=10.0,
33
            tc_time_used=0.0, instrument_types=['1M0-SCICAM-SBIG']
34
        )
35
        self.rg_single = mixer.blend(RequestGroup, proposal=self.proposal, operator='SINGLE',
3✔
36
                                     observation_type=RequestGroup.NORMAL)
37
        self.request = mixer.blend(Request, request_group=self.rg_single)
3✔
38
        self.configuration = mixer.blend(
3✔
39
            Configuration, request=self.request, instrument_type='1M0-SCICAM-SBIG', type='EXPOSE'
40
        )
41
        self.instrument_config = mixer.blend(
3✔
42
            InstrumentConfig, configuration=self.configuration, exposure_time=600, exposure_count=2,
43
            optical_elements={'filter': 'blah'}, mode='1m0_sbig_2', extra_params={'bin_x': 2, 'bin_y': 2}
44
        )
45
        self.acquisition_config = mixer.blend(AcquisitionConfig, configuration=self.configuration)
3✔
46
        self.guiding_config = mixer.blend(GuidingConfig, configuration=self.configuration)
3✔
47
        self.window = mixer.blend(
3✔
48
            Window, request=self.request, start=datetime(2016, 10, 1, tzinfo=timezone.utc),
49
            end=datetime(2016, 10, 8, tzinfo=timezone.utc)
50
        )
51
        mixer.blend(
3✔
52
            Target, configuration=self.configuration, type='ICRS', ra=22, dec=-33, proper_motion_ra=0.0,
53
            proper_motion_dec=0.0
54
        )
55
        self.location = mixer.blend(Location, request=self.request, telescope_class='1m0')
3✔
56
        mixer.blend(Constraints, configuration=self.configuration)
3✔
57

58

59
class TestRequestIntervals(BaseSetupRequest):
3✔
60
    def test_request_intervals_for_one_week(self):
3✔
61
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
62

63
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
64
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
65
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
66
                            datetime(2016, 10, 2, 3, 19, 9, 181040, tzinfo=timezone.utc)),
67
                           (datetime(2016, 10, 2, 19, 9, 19, 241762, tzinfo=timezone.utc),
68
                            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
69
                           (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
70
                            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
71
                           (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
72
                            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
73
                           (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
74
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
75
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
76
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
77
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
78
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
79

80
        self.assertEqual(intervals, truth_intervals)
3✔
81

82
    def test_get_largest_interval_culls_ones_before_now(self):
3✔
83
        # switch the window to be before the now time of 2016-9-1
84
        self.window.start = datetime(2016, 8, 21, tzinfo=timezone.utc)
3✔
85
        self.window.end = datetime(2016, 8, 31, tzinfo=timezone.utc)
3✔
86
        self.window.save()
3✔
87
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict())
3✔
88
        self.assertGreater(get_largest_interval(intervals).total_seconds(), 0)
3✔
89
        self.assertEqual(get_largest_interval(intervals, exclude_past=True).total_seconds(), 0)
3✔
90

91
    def test_request_intervals_for_staff_at_location(self):
3✔
92
        request_dict = self.request.as_dict()
3✔
93
        request_dict['location']['site'] = 'tst'
3✔
94
        request_dict['location']['enclosure'] = 'domd'
3✔
95
        request_dict['location']['telescope'] = '1m0a'
3✔
96
        request_dict['configurations'][0]['instrument_type'] = '1M0-SCICAM-SBAG'
3✔
97
        intervals = get_filtered_rise_set_intervals_by_site(request_dict, is_staff=False).get('tst', [])
3✔
98
        self.assertEqual(intervals, [])
3✔
99
        intervals = get_filtered_rise_set_intervals_by_site(request_dict, is_staff=True).get('tst', [])
3✔
100
        self.assertNotEqual(intervals, [])
3✔
101

102
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
103
    def test_request_intervals_for_one_week_removes_downtime(self, downtime_data):
3✔
104
        downtime_data.return_value = [{'start': '2016-10-01T22:00:00Z',
3✔
105
                                       'end': '2016-10-03T00:00:00Z',
106
                                       'site': 'tst',
107
                                       'enclosure': 'doma',
108
                                       'telescope': '1m0a',
109
                                       'instrument_type': '',
110
                                       'reason': 'Whatever'},
111
                                      {'start': '2016-10-01T22:00:00Z',
112
                                       'end': '2016-10-03T00:00:00Z',
113
                                       'site': 'tst',
114
                                       'enclosure': 'domb',
115
                                       'telescope': '1m0a',
116
                                       'instrument_type': '',
117
                                       'reason': 'Whatever'}
118
                                      ]
119

120
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
121

122
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
123
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
124
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
125
                            datetime(2016, 10, 1, 22, 0, 0, tzinfo=timezone.utc)),
126
                           (datetime(2016, 10, 3, 0, 0, 0, tzinfo=timezone.utc),
127
                            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
128
                           (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
129
                            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
130
                           (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
131
                            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
132
                           (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
133
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
134
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
135
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
136
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
137
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
138

139
        self.assertEqual(intervals, truth_intervals)
3✔
140

141
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
142
    def test_request_intervals_for_one_week_removes_downtime_for_instrument_type(self, downtime_data):
3✔
143
        downtime_data.return_value = [{'start': '2016-10-01T22:00:00Z',
3✔
144
                                       'end': '2016-10-03T00:00:00Z',
145
                                       'site': 'tst',
146
                                       'enclosure': 'doma',
147
                                       'telescope': '1m0a',
148
                                       'instrument_type': '1M0-SCICAM-SBIG',
149
                                       'reason': 'Whatever'},
150
                                      {'start': '2016-10-01T22:00:00Z',
151
                                       'end': '2016-10-03T00:00:00Z',
152
                                       'site': 'tst',
153
                                       'enclosure': 'domb',
154
                                       'telescope': '1m0a',
155
                                       'instrument_type': '1M0-SCICAM-SBIG',
156
                                       'reason': 'Whatever'},
157
                                      {'start': '2016-10-04T00:00:00Z',
158
                                       'end': '2016-10-08T00:00:00Z',
159
                                       'site': 'tst',
160
                                       'enclosure': 'doma',
161
                                       'telescope': '1m0a',
162
                                       'instrument_type': 'non-existent',
163
                                       'reason': 'Whatever'},
164
                                      {'start': '2016-10-04T00:00:00Z',
165
                                       'end': '2016-10-08T00:00:00Z',
166
                                       'site': 'tst',
167
                                       'enclosure': 'domb',
168
                                       'telescope': '1m0a',
169
                                       'instrument_type': 'non-existent',
170
                                       'reason': 'Whatever'},
171
                                      ]
172

173
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
174

175
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
176
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
177
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
178
                            datetime(2016, 10, 1, 22, 0, 0, tzinfo=timezone.utc)),
179
                           (datetime(2016, 10, 3, 0, 0, 0, tzinfo=timezone.utc),
180
                            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
181
                           (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
182
                            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
183
                           (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
184
                            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
185
                           (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
186
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
187
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
188
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
189
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
190
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
191

192
        self.assertEqual(intervals, truth_intervals)
3✔
193

194
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
195
    def test_request_intervals_for_one_week_removes_lots_of_downtime(self, downtime_data):
3✔
196
        downtime_data.return_value = [{'start': '2016-10-01T22:00:00Z',
3✔
197
                                       'end': '2016-10-03T00:00:00Z',
198
                                       'site': 'tst',
199
                                       'enclosure': 'doma',
200
                                       'telescope': '1m0a',
201
                                       'instrument_type': '',
202
                                       'reason': 'Whatever'},
203
                                      {'start': '2016-10-01T22:00:00Z',
204
                                       'end': '2016-10-03T00:00:00Z',
205
                                       'site': 'tst',
206
                                       'enclosure': 'domb',
207
                                       'telescope': '1m0a',
208
                                       'instrument_type': '',
209
                                       'reason': 'Whatever'},
210
                                      {'start': '2016-10-07T12:00:00Z',
211
                                       'end': '2017-10-03T00:00:00Z',
212
                                       'site': 'tst',
213
                                       'enclosure': 'doma',
214
                                       'telescope': '1m0a',
215
                                       'instrument_type': '',
216
                                       'reason': 'Whatever'},
217
                                      {'start': '2016-10-07T12:00:00Z',
218
                                       'end': '2017-10-03T00:00:00Z',
219
                                       'site': 'tst',
220
                                       'enclosure': 'domb',
221
                                       'telescope': '1m0a',
222
                                       'instrument_type': '',
223
                                       'reason': 'Whatever'},
224
                                      {'start': '2016-10-02T12:00:00Z',
225
                                       'end': '2016-10-06T00:00:00Z',
226
                                       'site': 'tst',
227
                                       'enclosure': 'doma',
228
                                       'telescope': '1m0a',
229
                                       'instrument_type': '',
230
                                       'reason': 'Whatever'},
231
                                      {'start': '2016-10-02T12:00:00Z',
232
                                       'end': '2016-10-06T00:00:00Z',
233
                                       'site': 'tst',
234
                                       'enclosure': 'domb',
235
                                       'telescope': '1m0a',
236
                                       'instrument_type': '',
237
                                       'reason': 'Whatever'},
238
                                      ]
239

240
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
241

242
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
243
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
244
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
245
                            datetime(2016, 10, 1, 22, 0, 0, tzinfo=timezone.utc)),
246
                           (datetime(2016, 10, 6, 0, 0, 0, tzinfo=timezone.utc),
247
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
248
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
249
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc))]
250

251
        self.assertEqual(intervals, truth_intervals)
3✔
252

253
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
254
    def test_request_intervals_for_one_week_all_downtime(self, downtime_data):
3✔
255
        downtime_data.return_value = [{'start': '2016-09-01T22:00:00Z',
3✔
256
                                       'end': '2016-11-03T00:00:00Z',
257
                                       'site': 'tst',
258
                                       'enclosure': 'doma',
259
                                       'telescope': '1m0a',
260
                                       'instrument_type': '',
261
                                       'reason': 'Whatever'},
262
                                      {'start': '2016-09-01T22:00:00Z',
263
                                       'end': '2016-11-03T00:00:00Z',
264
                                       'site': 'tst',
265
                                       'enclosure': 'domb',
266
                                       'telescope': '1m0a',
267
                                       'instrument_type': '',
268
                                       'reason': 'Whatever'}
269
                                      ]
270

271
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
272

273
        truth_intervals = []
3✔
274

275
        self.assertEqual(intervals, truth_intervals)
3✔
276

277
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
278
    def test_request_intervals_for_one_week_all_downtime_on_different_instrument_type(self, downtime_data):
3✔
279
        downtime_data.return_value = [{'start': '2016-09-01T22:00:00Z',
3✔
280
                                       'end': '2016-11-03T00:00:00Z',
281
                                       'site': 'tst',
282
                                       'enclosure': 'doma',
283
                                       'telescope': '1m0a',
284
                                       'instrument_type': 'non-existent',
285
                                       'reason': 'Whatever'},
286
                                      {'start': '2016-09-01T22:00:00Z',
287
                                       'end': '2016-11-03T00:00:00Z',
288
                                       'site': 'tst',
289
                                       'enclosure': 'domb',
290
                                       'telescope': '1m0a',
291
                                       'instrument_type': 'non-existent',
292
                                       'reason': 'Whatever'}
293
                                      ]
294

295
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
296

297
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
298
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
299
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
300
                            datetime(2016, 10, 2, 3, 19, 9, 181040, tzinfo=timezone.utc)),
301
                           (datetime(2016, 10, 2, 19, 9, 19, 241762, tzinfo=timezone.utc),
302
                            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
303
                           (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
304
                            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
305
                           (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
306
                            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
307
                           (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
308
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
309
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
310
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
311
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
312
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
313

314
        self.assertEqual(intervals, truth_intervals)
3✔
315

316
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
317
    def test_request_intervals_for_one_week_downtime_out_of_range(self, downtime_data):
3✔
318
        downtime_data.return_value = [{'start': '2016-09-01T22:00:00Z',
3✔
319
                                       'end': '2016-10-01T00:00:00Z',
320
                                       'site': 'tst',
321
                                       'enclosure': 'doma',
322
                                       'telescope': '1m0a',
323
                                       'instrument_type': '',
324
                                       'reason': 'Whatever'},
325
                                      {'start': '2016-09-01T22:00:00Z',
326
                                       'end': '2016-10-01T00:00:00Z',
327
                                       'site': 'tst',
328
                                       'enclosure': 'domb',
329
                                       'telescope': '1m0a',
330
                                       'instrument_type': '',
331
                                       'reason': 'Whatever'},
332
                                      {'start': '2016-10-08T00:00:00Z',
333
                                       'end': '2016-11-01T00:00:00Z',
334
                                       'site': 'tst',
335
                                       'enclosure': 'doma',
336
                                       'telescope': '1m0a',
337
                                       'instrument_type': '',
338
                                       'reason': 'Whatever'},
339
                                      {'start': '2016-10-08T00:00:00Z',
340
                                       'end': '2016-11-01T00:00:00Z',
341
                                       'site': 'tst',
342
                                       'enclosure': 'domb',
343
                                       'telescope': '1m0a',
344
                                       'instrument_type': '',
345
                                       'reason': 'Whatever'}
346
                                      ]
347

348
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
349

350
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
351
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
352
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
353
                            datetime(2016, 10, 2, 3, 19, 9, 181040, tzinfo=timezone.utc)),
354
                           (datetime(2016, 10, 2, 19, 9, 19, 241762, tzinfo=timezone.utc),
355
                            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
356
                           (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
357
                            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
358
                           (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
359
                            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
360
                           (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
361
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
362
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
363
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
364
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
365
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
366

367
        self.assertEqual(intervals, truth_intervals)
3✔
368

369
    @patch('observation_portal.common.downtimedb.DowntimeDB._get_downtime_data')
3✔
370
    def test_request_intervals_for_one_week_overlapping_downtime(self, downtime_data):
3✔
371
        downtime_data.return_value = [{'start': '2016-10-01T22:00:00Z',
3✔
372
                                       'end': '2016-10-04T00:00:00Z',
373
                                       'site': 'tst',
374
                                       'enclosure': 'doma',
375
                                       'telescope': '1m0a',
376
                                       'instrument_type': '',
377
                                       'reason': 'Whatever'},
378
                                      {'start': '2016-10-01T22:00:00Z',
379
                                       'end': '2016-10-04T00:00:00Z',
380
                                       'site': 'tst',
381
                                       'enclosure': 'domb',
382
                                       'telescope': '1m0a',
383
                                       'instrument_type': '',
384
                                       'reason': 'Whatever'},
385
                                      {'start': '2016-10-02T00:00:00Z',
386
                                       'end': '2016-10-03T00:00:00Z',
387
                                       'site': 'tst',
388
                                       'enclosure': 'doma',
389
                                       'telescope': '1m0a',
390
                                       'instrument_type': '',
391
                                       'reason': 'Whatever'},
392
                                      {'start': '2016-10-02T00:00:00Z',
393
                                       'end': '2016-10-03T00:00:00Z',
394
                                       'site': 'tst',
395
                                       'enclosure': 'domb',
396
                                       'telescope': '1m0a',
397
                                       'instrument_type': '',
398
                                       'reason': 'Whatever'},
399
                                      {'start': '2016-10-02T00:00:00Z',
400
                                       'end': '2016-10-06T00:00:00Z',
401
                                       'site': 'tst',
402
                                       'enclosure': 'doma',
403
                                       'telescope': '1m0a',
404
                                       'instrument_type': '',
405
                                       'reason': 'Whatever'},
406
                                      {'start': '2016-10-02T00:00:00Z',
407
                                       'end': '2016-10-06T00:00:00Z',
408
                                       'site': 'tst',
409
                                       'enclosure': 'domb',
410
                                       'telescope': '1m0a',
411
                                       'instrument_type': '',
412
                                       'reason': 'Whatever'}
413
                                      ]
414

415
        intervals = get_filtered_rise_set_intervals_by_site(self.request.as_dict()).get('tst', [])
3✔
416

417
        truth_intervals = [(datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
3✔
418
                            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
419
                           (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
420
                            datetime(2016, 10, 1, 22, 0, 0, tzinfo=timezone.utc)),
421
                           (datetime(2016, 10, 6, 0, 0, 0, tzinfo=timezone.utc),
422
                            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
423
                           (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
424
                            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
425
                           (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
426
                            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))]
427

428
        self.assertEqual(intervals, truth_intervals)
3✔
429

430

431
class TestMultipleTargetRequestIntervals(BaseSetupRequest):
3✔
432
    def test_request_intervals_for_multiple_targets_intersected(self):
3✔
433
        request_dict = self.request.as_dict()
3✔
434
        intervals = get_filtered_rise_set_intervals_by_site(request_dict).get('tst', [])
3✔
435
        truth_intervals = [
3✔
436
            (datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc),
437
            datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
438
            (datetime(2016, 10, 1, 19, 13, 14, 944205, tzinfo=timezone.utc),
439
            datetime(2016, 10, 2, 3, 19, 9, 181040, tzinfo=timezone.utc)),
440
            (datetime(2016, 10, 2, 19, 9, 19, 241762, tzinfo=timezone.utc),
441
            datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
442
            (datetime(2016, 10, 3, 19, 5, 23, 539011, tzinfo=timezone.utc),
443
            datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
444
            (datetime(2016, 10, 4, 19, 1, 27, 835928, tzinfo=timezone.utc),
445
            datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
446
            (datetime(2016, 10, 5, 18, 57, 32, 132481, tzinfo=timezone.utc),
447
            datetime(2016, 10, 6, 3, 12, 5, 895932, tzinfo=timezone.utc)),
448
            (datetime(2016, 10, 6, 18, 53, 36, 428629, tzinfo=timezone.utc),
449
            datetime(2016, 10, 7, 3, 8, 10, 183626, tzinfo=timezone.utc)),
450
            (datetime(2016, 10, 7, 18, 49, 40, 724307, tzinfo=timezone.utc),
451
            datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc))
452
        ]
453

454
        self.assertEqual(intervals, truth_intervals)
3✔
455
        # now create get the intervals for a request with the second target
456
        configuration2 = deepcopy(request_dict['configurations'][0])
3✔
457
        configuration2['target']['ra'] = 85.0  # change the RA so the target has different visibility
3✔
458
        request_dict2 = deepcopy(request_dict)
3✔
459
        request_dict2['configurations'][0] = configuration2
3✔
460
        intervals2 = get_filtered_rise_set_intervals_by_site(request_dict2).get('tst', [])
3✔
461
        truth_intervals2 = [
3✔
462
            (datetime(2016, 10, 1, 0, 0, tzinfo=timezone.utc), datetime(2016, 10, 1, 3, 20, 31, 366820, tzinfo=timezone.utc)),
463
            (datetime(2016, 10, 1, 23, 24, 4, 392218, tzinfo=timezone.utc), datetime(2016, 10, 2, 3, 19, 9, 181040, tzinfo=timezone.utc)),
464
            (datetime(2016, 10, 2, 23, 20, 8, 717423, tzinfo=timezone.utc), datetime(2016, 10, 3, 3, 17, 47, 117329, tzinfo=timezone.utc)),
465
            (datetime(2016, 10, 3, 23, 16, 13, 42308, tzinfo=timezone.utc), datetime(2016, 10, 4, 3, 16, 25, 202612, tzinfo=timezone.utc)),
466
            (datetime(2016, 10, 4, 23, 12, 17, 366627, tzinfo=timezone.utc), datetime(2016, 10, 5, 3, 15, 3, 464340, tzinfo=timezone.utc)),
467
            (datetime(2016, 10, 5, 23, 8, 21, 690204, tzinfo=timezone.utc), datetime(2016, 10, 6, 3, 13, 41, 930536, tzinfo=timezone.utc)),
468
            (datetime(2016, 10, 6, 23, 4, 26, 12943, tzinfo=timezone.utc), datetime(2016, 10, 7, 3, 12, 20, 629833, tzinfo=timezone.utc)),
469
            (datetime(2016, 10, 7, 23, 0, 30, 334810, tzinfo=timezone.utc), datetime(2016, 10, 8, 0, 0, tzinfo=timezone.utc)),
470
        ]
471
        self.assertEqual(intervals2, truth_intervals2)
3✔
472

473
        # now get the intervals for both targets combined in the request and verify they are intersected
474
        request_dict3 = deepcopy(request_dict)
3✔
475
        request_dict3['configurations'].append(configuration2)
3✔
476
        intervals3 = get_filtered_rise_set_intervals_by_site(request_dict3).get('tst', [])
3✔
477
        truth_intervals_combined = Intervals(truth_intervals).intersect([Intervals(truth_intervals2)]).toTupleList()
3✔
478
        self.assertEqual(intervals3, truth_intervals_combined)
3✔
479

480
    def test_request_intervals_for_multiple_targets_empty_if_one_is_empty(self):
3✔
481
        request_dict = self.request.as_dict()
3✔
482

483
        # now create get the intervals for a request with the second target that isn't visible
484
        configuration2 = deepcopy(request_dict['configurations'][0])
3✔
485
        configuration2['target']['dec'] = 70.0  # change the DEC so the target isn't visible
3✔
486
        request_dict2 = deepcopy(request_dict)
3✔
487
        request_dict2['configurations'][0] = configuration2
3✔
488
        intervals = get_filtered_rise_set_intervals_by_site(request_dict2).get('tst', [])
3✔
489
        truth_intervals = [
3✔
490
        ]
491
        self.assertEqual(intervals, truth_intervals)
3✔
492

493
        # now get the intervals for both targets combined in the request and verify they are intersected and empty
494
        request_dict3 = deepcopy(request_dict)
3✔
495
        request_dict3['configurations'].append(configuration2)
3✔
496
        intervals3 = get_filtered_rise_set_intervals_by_site(request_dict3).get('tst', [])
3✔
497
        self.assertEqual(intervals3, [])
3✔
498

499
    def test_airmass_for_multiple_targets_averaged(self):
3✔
500
        request_dict = self.request.as_dict()
3✔
501
        airmasses = get_airmasses_for_request_at_sites(request_dict)
3✔
502

503
        # now create get the intervals for a request with the second target
504
        configuration2 = deepcopy(request_dict['configurations'][0])
3✔
505
        configuration2['target']['ra'] = 85.0  # change the RA so the target has different visibility
3✔
506
        request_dict2 = deepcopy(request_dict)
3✔
507
        request_dict2['configurations'][0] = configuration2
3✔
508
        airmasses2 = get_airmasses_for_request_at_sites(request_dict2)
3✔
509

510
        # now get the intervals for both targets combined in the request and verify they are intersected
511
        request_dict3 = deepcopy(request_dict)
3✔
512
        request_dict3['configurations'].append(configuration2)
3✔
513
        airmasses_combined = get_airmasses_for_request_at_sites(request_dict3)
3✔
514

515
        # The first few intervals should at least match up in time, so compare those
516
        for i in range(4):
3✔
517
            self.assertNotEqual(airmasses['airmass_data']['tst']['airmasses'][i], airmasses2['airmass_data']['tst']['airmasses'][i])
3✔
518
            average_airmass = (airmasses['airmass_data']['tst']['airmasses'][i] +  airmasses2['airmass_data']['tst']['airmasses'][i]) / 2.0
3✔
519
            self.assertEqual(average_airmass, airmasses_combined['airmass_data']['tst']['airmasses'][i])
3✔
520
        average_airmass_limit = (airmasses['airmass_limit'] + airmasses2['airmass_limit']) / 2.0
3✔
521
        self.assertEqual(average_airmass_limit, airmasses_combined['airmass_limit'])
3✔
522

523

524
class TestRequestAirmass(BaseSetupRequest):
3✔
525
    def test_airmass_calculation(self):
3✔
526
        airmasses = get_airmasses_for_request_at_sites(self.request.as_dict())
3✔
527

528
        # Should be no data betwee 3:30AM and 18:30PM acording to pure rise set for this target, so verify that
529
        expected_null_range = (datetime(2016, 10, 1, 3, 30, 0), datetime(2016, 10, 1, 18, 30, 0))
3✔
530

531
        for airmass_time in airmasses['airmass_data']['tst']['times']:
3✔
532
            atime = datetime.strptime(airmass_time, '%Y-%m-%dT%H:%M')
3✔
533
            if atime > expected_null_range[0] and atime < expected_null_range[1]:
3✔
UNCOV
534
                self.fail("Should not get airmass ({}) within range {}".format(atime, expected_null_range))
×
535

536
    def test_airmass_calculation_empty(self):
3✔
537
        self.location.site = 'cpt'
3✔
538
        self.location.save()
3✔
539
        airmasses = get_airmasses_for_request_at_sites(self.request.as_dict())
3✔
540

541
        self.assertFalse(airmasses['airmass_data'])
3✔
542

543
    def test_satellite_target_types_airmass_calc_is_empty(self):
3✔
544
        self.configuration.target.type = 'SATELLITE'
3✔
545
        self.configuration.target.altitude = 90
3✔
546
        self.configuration.target.azimuth = 0
3✔
547
        self.configuration.target.diff_altitude_rate = 0.01
3✔
548
        self.configuration.target.diff_azimuth_rate = 0.01
3✔
549
        self.configuration.target.diff_epoch = 15000
3✔
550
        self.configuration.target.diff_altitude_acceleration = 0.001
3✔
551
        self.configuration.target.diff_azimuth_acceleration = 0.001
3✔
552
        self.configuration.target.save()
3✔
553
        airmasses = get_airmasses_for_request_at_sites(self.request.as_dict())
3✔
554
        self.assertFalse(airmasses['airmass_data'])
3✔
555

556
    def test_hour_angle_target_types_airmass_calc_is_empty(self):
3✔
557
        self.configuration.target.type = 'HOUR_ANGLE'
3✔
558
        self.configuration.target.hour_angle = 0
3✔
559
        self.configuration.target.save()
3✔
560
        airmasses = get_airmasses_for_request_at_sites(self.request.as_dict())
3✔
561
        self.assertFalse(airmasses['airmass_data'])
3✔
562

563

564
class TestRequestTelescopeStates(TelescopeStatesFakeInput):
3✔
565
    def setUp(self):
3✔
566
        super().setUp()
3✔
567
        self.time_patcher = patch('observation_portal.requestgroups.serializers.timezone.now')
3✔
568
        self.mock_now = self.time_patcher.start()
3✔
569
        self.mock_now.return_value = datetime(2016, 10, 1, tzinfo=timezone.utc)
3✔
570
        self.proposal = mixer.blend(Proposal)
3✔
571
        semester = mixer.blend(Semester, id='2016B', start=datetime(2016, 9, 1, tzinfo=timezone.utc),
3✔
572
                               end=datetime(2016, 12, 31, tzinfo=timezone.utc)
573
                               )
574
        self.time_allocation_1m0 = mixer.blend(TimeAllocation, proposal=self.proposal, semester=semester,
3✔
575
                                               instrument_types=['1M0-SCICAM-SBIG'], std_allocation=100.0, std_time_used=0.0,
576
                                               rr_allocation=10, rr_time_used=0.0, ipp_limit=10.0,
577
                                               ipp_time_available=5.0, tc_time_available=10.0, tc_time_used=0.0)
578

579
        self.rg_single = mixer.blend(RequestGroup, proposal=self.proposal, operator='SINGLE',
3✔
580
                                     observation_type=RequestGroup.NORMAL)
581

582
        self.request = mixer.blend(Request, request_group=self.rg_single)
3✔
583

584
        self.configuration = mixer.blend(
3✔
585
            Configuration, request=self.request, instrument_type='1M0-SCICAM-SBIG', type='EXPOSE'
586
        )
587
        self.instrument_config = mixer.blend(
3✔
588
            InstrumentConfig, configuration=self.configuration, exposure_time=600, exposure_count=2,
589
            optical_elements={'filter': 'blah'}, mode='1m0_sbig_2', extra_params={'bin_x': 2, 'bin_y': 2}
590
        )
591
        self.acquisition_config = mixer.blend(AcquisitionConfig, configuration=self.configuration)
3✔
592
        self.guiding_config = mixer.blend(GuidingConfig, configuration=self.configuration)
3✔
593

594
        mixer.blend(Window, request=self.request, start=datetime(2016, 10, 1, tzinfo=timezone.utc),
3✔
595
                    end=datetime(2016, 10, 2, tzinfo=timezone.utc))
596

597
        mixer.blend(Target, configuration=self.configuration, type='ICRS', ra=22, dec=-33,
3✔
598
                    proper_motion_ra=0.0, proper_motion_dec=0.0)
599

600
        self.location = mixer.blend(Location, request=self.request, telescope_class='1m0')
3✔
601
        mixer.blend(Constraints, configuration=self.configuration, max_airmass=2.0)
3✔
602

603
    def tearDown(self):
3✔
604
        super().tearDown()
3✔
605
        self.time_patcher.stop()
3✔
606

607
        # super(BaseSetupRequest, self).tearDown()
608

609
    def test_telescope_states_calculation(self):
3✔
610
        telescope_states = get_telescope_states_for_request(self.request.as_dict())
3✔
611
        # Assert that telescope states were received for this request
612
        self.assertIn(self.tk1, telescope_states)
3✔
613
        self.assertIn(self.tk2, telescope_states)
3✔
614

615
        expected_start_of_night = datetime(2016, 10, 1, 18, 45, 49, 461652, tzinfo=timezone.utc)
3✔
616

617
        # These are the same states tested for similar times in the telescope_states test class
618
        doma_expected_available_state = {'telescope': 'tst.doma.1m0a',
3✔
619
                                         'event_type': 'AVAILABLE',
620
                                         'event_reason': 'Available for scheduling',
621
                                         'start': expected_start_of_night,
622
                                         'end': datetime(2016, 10, 1, 20, 44, 58, tzinfo=timezone.utc)
623
                                         }
624

625
        self.assertIn(doma_expected_available_state, telescope_states[self.tk1])
3✔
626

627
        domb_expected_available_state1 = {'telescope': 'tst.domb.1m0a',
3✔
628
                                          'event_type': 'AVAILABLE',
629
                                          'event_reason': 'Available for scheduling',
630
                                          'start': expected_start_of_night,
631
                                          'end': datetime(2016, 10, 1, 19, 24, 59, tzinfo=timezone.utc)
632
                                          }
633

634
        self.assertIn(domb_expected_available_state1, telescope_states[self.tk2])
3✔
635

636
        domb_expected_available_state2 = {'telescope': 'tst.domb.1m0a',
3✔
637
                                          'event_type': 'AVAILABLE',
638
                                          'event_reason': 'Available for scheduling',
639
                                          'start': datetime(2016, 10, 1, 20, 24, 59, tzinfo=timezone.utc),
640
                                          'end': datetime(2016, 10, 1, 20, 44, 58, tzinfo=timezone.utc)
641
                                          }
642

643
        self.assertIn(domb_expected_available_state2, telescope_states[self.tk2])
3✔
644

645
    def test_telescope_states_calculation_with_no_target(self):
3✔
646
        request_dict = self.request.as_dict()
3✔
647
        request_dict['configurations'][0]['target'] = {}
3✔
648

649
        telescope_states = get_telescope_states_for_request(request_dict)
3✔
650
        # Assert that telescope states were received for this request
651
        self.assertIn(self.tk1, telescope_states)
3✔
652
        self.assertIn(self.tk2, telescope_states)
3✔
653

654
        expected_start_of_night_doma = datetime(2016, 10, 1, 18, 24, 58, tzinfo=timezone.utc)
3✔
655
        expected_start_of_night_domb = datetime(2016, 10, 1, 18, 30, 0, tzinfo=timezone.utc)
3✔
656

657

658
        # These are the same states tested for similar times in the telescope_states test class
659
        doma_expected_available_state = {'telescope': 'tst.doma.1m0a',
3✔
660
                                         'event_type': 'AVAILABLE',
661
                                         'event_reason': 'Available for scheduling',
662
                                         'start': expected_start_of_night_doma,
663
                                         'end': datetime(2016, 10, 1, 20, 44, 58, tzinfo=timezone.utc)
664
                                         }
665

666
        self.assertIn(doma_expected_available_state, telescope_states[self.tk1])
3✔
667

668
        domb_expected_available_state1 = {'telescope': 'tst.domb.1m0a',
3✔
669
                                          'event_type': 'AVAILABLE',
670
                                          'event_reason': 'Available for scheduling',
671
                                          'start': expected_start_of_night_domb,
672
                                          'end': datetime(2016, 10, 1, 19, 24, 59, tzinfo=timezone.utc)
673
                                          }
674

675
        self.assertIn(domb_expected_available_state1, telescope_states[self.tk2])
3✔
676

677
        domb_expected_available_state2 = {'telescope': 'tst.domb.1m0a',
3✔
678
                                          'event_type': 'AVAILABLE',
679
                                          'event_reason': 'Available for scheduling',
680
                                          'start': datetime(2016, 10, 1, 20, 24, 59, tzinfo=timezone.utc),
681
                                          'end': datetime(2016, 10, 1, 20, 44, 58, tzinfo=timezone.utc)
682
                                          }
683

684
        self.assertIn(domb_expected_available_state2, telescope_states[self.tk2])
3✔
685

686
    def test_telescope_states_empty(self):
3✔
687
        self.location.site = 'cpt'
3✔
688
        self.location.save()
3✔
689
        telescope_states = get_telescope_states_for_request(self.request.as_dict())
3✔
690

691
        self.assertEqual({}, telescope_states)
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc