Coverage for tests/test_efdUtils.py: 31%
123 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-27 11:10 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-27 11:10 +0000
1# This file is part of summit_utils.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Test cases for utils."""
24import unittest
25import lsst.utils.tests
26import astropy
27import pandas as pd
28import datetime
29import asyncio
30from astropy.time import Time
32from lsst.summit.utils.tmaUtils import TMAEvent, TMAState
34from lsst.summit.utils.efdUtils import (
35 getEfdData,
36 getMostRecentRowWithDataBefore,
37 makeEfdClient,
38 efdTimestampToAstropy,
39 astropyToEfdTimestamp,
40 clipDataToEvent,
41 # calcNextDay, # this is indirectly tested by test_getDayObsAsTimes()
42 getDayObsStartTime,
43 getDayObsEndTime,
44 getDayObsForTime,
45 getSubTopics,
46)
48HAS_EFD_CLIENT = True
49try:
50 import lsst_efd_client
51except ImportError:
52 HAS_EFD_CLIENT = False
55@unittest.skipIf(not HAS_EFD_CLIENT, "No EFD client available")
56@unittest.skip("Skipping until DM-40101 is resolved.")
57class EfdUtilsTestCase(lsst.utils.tests.TestCase):
59 @classmethod
60 def setUpClass(cls):
61 try:
62 cls.client = makeEfdClient()
63 except RuntimeError:
64 raise unittest.SkipTest("Could not instantiate an EFD client")
65 cls.dayObs = 20230531
66 # get a sample expRecord here to test expRecordToTimespan
67 cls.axisTopic = 'lsst.sal.MTMount.logevent_azimuthMotionState'
68 cls.timeSeriesTopic = 'lsst.sal.MTMount.azimuth'
69 cls.event = TMAEvent(
70 dayObs=20230531,
71 seqNum=27,
72 type=TMAState.TRACKING,
73 endReason=TMAState.SLEWING,
74 duration=0.47125244140625,
75 begin=Time(1685578353.2265284, scale='utc', format='unix'),
76 end=Time(1685578353.6977808, scale='utc', format='unix'),
77 blockInfo=None,
78 version=0,
79 _startRow=254,
80 _endRow=255,
81 )
83 def tearDown(self):
84 loop = asyncio.get_event_loop()
85 loop.run_until_complete(self.client.influx_client.close())
87 def test_makeEfdClient(self):
88 self.assertIsInstance(self.client, lsst_efd_client.efd_helper.EfdClient)
90 def test_getDayObsAsTimes(self):
91 """This tests getDayObsStartTime and getDayObsEndTime explicitly,
92 but the days we loop over are chosen to test calcNextDay() which is
93 called by getDayObsEndTime().
94 """
95 for dayObs in (
96 self.dayObs, # the nominal value
97 20200228, # day before end of Feb on a leap year
98 20200229, # end of Feb on a leap year
99 20210227, # day before end of Feb on a non-leap year
100 20200228, # end of Feb on a non-leap year
101 20200430, # end of a month with 30 days
102 20200530, # end of a month with 31 days
103 20201231, # year rollover
104 ):
105 dayStart = getDayObsStartTime(dayObs)
106 self.assertIsInstance(dayStart, astropy.time.Time)
108 dayEnd = getDayObsEndTime(dayObs)
109 self.assertIsInstance(dayStart, astropy.time.Time)
111 self.assertGreater(dayEnd, dayStart)
112 self.assertEqual(dayEnd.jd, dayStart.jd + 1)
114 def test_getSubTopics(self):
115 subTopics = getSubTopics(self.client, 'lsst.sal.MTMount')
116 self.assertIsInstance(subTopics, list)
117 self.assertGreater(len(subTopics), 0)
119 subTopics = getSubTopics(self.client, 'fake.topics.does.not.exist')
120 self.assertIsInstance(subTopics, list)
121 self.assertEqual(len(subTopics), 0)
123 def test_getEfdData(self):
124 dayStart = getDayObsStartTime(self.dayObs)
125 dayEnd = getDayObsEndTime(self.dayObs)
126 oneDay = datetime.timedelta(hours=24)
127 # twelveHours = datetime.timedelta(hours=12)
129 # test the dayObs interface
130 dayObsData = getEfdData(self.client, self.axisTopic, dayObs=self.dayObs)
131 self.assertIsInstance(dayObsData, pd.DataFrame)
133 # test the starttime interface
134 dayStartData = getEfdData(self.client, self.axisTopic, begin=dayStart, timespan=oneDay)
135 self.assertIsInstance(dayStartData, pd.DataFrame)
137 # check they're equal
138 self.assertTrue(dayObsData.equals(dayStartData))
140 # test the starttime interface with an endtime
141 dayEnd = getDayObsEndTime(self.dayObs)
142 dayStartEndData = getEfdData(self.client, self.axisTopic, begin=dayStart, end=dayEnd)
143 self.assertTrue(dayObsData.equals(dayStartEndData))
145 # test event
146 # note that here we're going to clip to an event and pad things, so
147 # we want to use the timeSeriesTopic not the states, so that there's
148 # plenty of rows to test the padding is actually working
149 eventData = getEfdData(self.client, self.timeSeriesTopic, event=self.event)
150 self.assertIsInstance(dayObsData, pd.DataFrame)
152 # test padding options
153 padded = getEfdData(self.client, self.timeSeriesTopic, event=self.event, prePadding=1, postPadding=2)
154 self.assertGreater(len(padded), len(eventData))
155 startTimeDiff = (efdTimestampToAstropy(eventData.iloc[0]['private_efdStamp']) -
156 efdTimestampToAstropy(padded.iloc[0]['private_efdStamp']))
157 endTimeDiff = (efdTimestampToAstropy(padded.iloc[-1]['private_efdStamp']) -
158 efdTimestampToAstropy(eventData.iloc[-1]['private_efdStamp']))
160 self.assertGreater(startTimeDiff.sec, 0)
161 self.assertLess(startTimeDiff.sec, 1.1) # padding isn't super exact, so give a little wiggle room
162 self.assertGreater(endTimeDiff.sec, 0)
163 self.assertLess(endTimeDiff.sec, 2.1) # padding isn't super exact, so give a little wiggle room
165 with self.assertRaises(ValueError):
166 # not enough info to constrain
167 _ = getEfdData(self.client, self.axisTopic)
168 # dayObs supplied and a start time is not allowed
169 _ = getEfdData(self.client, self.axisTopic, dayObs=self.dayObs, begin=dayStart)
170 # dayObs supplied and a stop time is not allowed
171 _ = getEfdData(self.client, self.axisTopic, dayObs=self.dayObs, end=dayEnd)
172 # dayObs supplied and timespan is not allowed
173 _ = getEfdData(self.client, self.axisTopic, dayObs=self.dayObs, timespan=oneDay)
174 # being alone is not allowed
175 _ = getEfdData(self.client, self.axisTopic, begin=self.dayObs)
176 # good query, except the topic doesn't exist
177 _ = getEfdData(self.client, 'badTopic', begin=dayStart, end=dayEnd)
179 def test_getMostRecentRowWithDataBefore(self):
180 time = Time(1687845854.736784, scale='utc', format='unix')
181 rowData = getMostRecentRowWithDataBefore(self.client,
182 "lsst.sal.MTM1M3.logevent_forceActuatorState",
183 time)
184 self.assertIsInstance(rowData, pd.Series)
186 stateTime = efdTimestampToAstropy(rowData['private_efdStamp'])
187 self.assertLess(stateTime, time)
189 def test_efdTimestampToAstropy(self):
190 time = efdTimestampToAstropy(1687845854.736784)
191 self.assertIsInstance(time, astropy.time.Time)
192 return
194 def test_astropyToEfdTimestamp(self):
195 time = Time(1687845854.736784, scale='utc', format='unix')
196 efdTimestamp = astropyToEfdTimestamp(time)
197 self.assertIsInstance(efdTimestamp, float)
198 return
200 def test_clipDataToEvent(self):
201 # get 10 mins of data either side of the event we'll clip to
202 duration = datetime.timedelta(seconds=10*60)
203 queryBegin = self.event.begin - duration
204 queryEnd = self.event.end + duration
205 dayObsData = getEfdData(self.client, 'lsst.sal.MTMount.azimuth', begin=queryBegin, end=queryEnd)
207 # clip the data, and check it's shorter, non-zero, and falls in the
208 # right time range
209 clippedData = clipDataToEvent(dayObsData, self.event)
211 self.assertIsInstance(clippedData, pd.DataFrame)
212 self.assertGreater(len(clippedData), 0)
213 self.assertLess(len(clippedData), len(dayObsData))
215 dataStart = efdTimestampToAstropy(clippedData.iloc[0]['private_efdStamp'])
216 dataEnd = efdTimestampToAstropy(clippedData.iloc[-1]['private_efdStamp'])
218 self.assertGreaterEqual(dataStart, self.event.begin)
219 self.assertLessEqual(dataEnd, self.event.end)
220 return
222 def test_getDayObsForTime(self):
223 pydate = datetime.datetime(2023, 2, 5, 13, 30, 1)
224 time = Time(pydate)
225 dayObs = getDayObsForTime(time)
226 self.assertEqual(dayObs, 20230205)
228 pydate = datetime.datetime(2023, 2, 5, 11, 30, 1)
229 time = Time(pydate)
230 dayObs = getDayObsForTime(time)
231 self.assertEqual(dayObs, 20230204)
232 return
235class TestMemory(lsst.utils.tests.MemoryTestCase):
236 pass
239def setup_module(module):
240 lsst.utils.tests.init()
243if __name__ == "__main__": 243 ↛ 244line 243 didn't jump to line 244, because the condition on line 243 was never true
244 lsst.utils.tests.init()
245 unittest.main()