Coverage for tests/test_efdUtils.py: 31%

123 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-08-06 04:34 +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/>. 

21 

22"""Test cases for utils.""" 

23 

24import unittest 

25import lsst.utils.tests 

26import astropy 

27import pandas as pd 

28import datetime 

29import asyncio 

30from astropy.time import Time 

31 

32from lsst.summit.utils.tmaUtils import TMAEvent, TMAState 

33 

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) 

47 

48HAS_EFD_CLIENT = True 

49try: 

50 import lsst_efd_client 

51except ImportError: 

52 HAS_EFD_CLIENT = False 

53 

54 

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): 

58 

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 ) 

82 

83 def tearDown(self): 

84 loop = asyncio.get_event_loop() 

85 loop.run_until_complete(self.client.influx_client.close()) 

86 

87 def test_makeEfdClient(self): 

88 self.assertIsInstance(self.client, lsst_efd_client.efd_helper.EfdClient) 

89 

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) 

107 

108 dayEnd = getDayObsEndTime(dayObs) 

109 self.assertIsInstance(dayStart, astropy.time.Time) 

110 

111 self.assertGreater(dayEnd, dayStart) 

112 self.assertEqual(dayEnd.jd, dayStart.jd + 1) 

113 

114 def test_getSubTopics(self): 

115 subTopics = getSubTopics(self.client, 'lsst.sal.MTMount') 

116 self.assertIsInstance(subTopics, list) 

117 self.assertGreater(len(subTopics), 0) 

118 

119 subTopics = getSubTopics(self.client, 'fake.topics.does.not.exist') 

120 self.assertIsInstance(subTopics, list) 

121 self.assertEqual(len(subTopics), 0) 

122 

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) 

128 

129 # test the dayObs interface 

130 dayObsData = getEfdData(self.client, self.axisTopic, dayObs=self.dayObs) 

131 self.assertIsInstance(dayObsData, pd.DataFrame) 

132 

133 # test the starttime interface 

134 dayStartData = getEfdData(self.client, self.axisTopic, begin=dayStart, timespan=oneDay) 

135 self.assertIsInstance(dayStartData, pd.DataFrame) 

136 

137 # check they're equal 

138 self.assertTrue(dayObsData.equals(dayStartData)) 

139 

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)) 

144 

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) 

151 

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'])) 

159 

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 

164 

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) 

178 

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) 

185 

186 stateTime = efdTimestampToAstropy(rowData['private_efdStamp']) 

187 self.assertLess(stateTime, time) 

188 

189 def test_efdTimestampToAstropy(self): 

190 time = efdTimestampToAstropy(1687845854.736784) 

191 self.assertIsInstance(time, astropy.time.Time) 

192 return 

193 

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 

199 

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) 

206 

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) 

210 

211 self.assertIsInstance(clippedData, pd.DataFrame) 

212 self.assertGreater(len(clippedData), 0) 

213 self.assertLess(len(clippedData), len(dayObsData)) 

214 

215 dataStart = efdTimestampToAstropy(clippedData.iloc[0]['private_efdStamp']) 

216 dataEnd = efdTimestampToAstropy(clippedData.iloc[-1]['private_efdStamp']) 

217 

218 self.assertGreaterEqual(dataStart, self.event.begin) 

219 self.assertLessEqual(dataEnd, self.event.end) 

220 return 

221 

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) 

227 

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 

233 

234 

235class TestMemory(lsst.utils.tests.MemoryTestCase): 

236 pass 

237 

238 

239def setup_module(module): 

240 lsst.utils.tests.init() 

241 

242 

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()