Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of astro_metadata_translator. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the LICENSE file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12import unittest 

13import os.path 

14 

15from astro_metadata_translator import merge_headers, fix_header, HscTranslator 

16from astro_metadata_translator.tests import read_test_file 

17from astro_metadata_translator import DecamTranslator 

18 

19TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

20 

21 

22class NotDecamTranslator(DecamTranslator): 

23 """This is a DECam translator with override list of header corrections.""" 

24 name = None 

25 

26 @classmethod 

27 def fix_header(cls, header, instrument, obsid, filename=None): 

28 header["DTSITE"] = "hi" 

29 return True 

30 

31 

32class AlsoNotDecamTranslator(DecamTranslator): 

33 """This is a DECam translator with override list of header corrections 

34 that fails.""" 

35 name = None 

36 

37 @classmethod 

38 def fix_header(cls, header, instrument, obsid, filename=None): 

39 raise RuntimeError("Failure to work something out from header") 

40 

41 

42class HeadersTestCase(unittest.TestCase): 

43 

44 def setUp(self): 

45 # Define reference headers 

46 self.h1 = dict( 

47 ORIGIN="LSST", 

48 KEY0=0, 

49 KEY1=1, 

50 KEY2=3, 

51 KEY3=3.1415, 

52 KEY4="a", 

53 ) 

54 

55 self.h2 = dict( 

56 ORIGIN="LSST", 

57 KEY0="0", 

58 KEY2=4, 

59 KEY5=42 

60 ) 

61 self.h3 = dict( 

62 ORIGIN="AUXTEL", 

63 KEY3=3.1415, 

64 KEY2=50, 

65 KEY5=42, 

66 ) 

67 self.h4 = dict( 

68 KEY6="New", 

69 KEY1="Exists", 

70 ) 

71 

72 # Add keys for sorting by time 

73 # Sorted order: h2, h1, h4, h3 

74 self.h1["MJD-OBS"] = 50000.0 

75 self.h2["MJD-OBS"] = 49000.0 

76 self.h3["MJD-OBS"] = 53000.0 

77 self.h4["MJD-OBS"] = 52000.0 

78 

79 def test_fail(self): 

80 with self.assertRaises(ValueError): 

81 merge_headers([self.h1, self.h2], mode="wrong") 

82 

83 with self.assertRaises(ValueError): 

84 merge_headers([]) 

85 

86 def test_one(self): 

87 merged = merge_headers([self.h1], mode="drop") 

88 self.assertEqual(merged, self.h1) 

89 

90 def test_merging_overwrite(self): 

91 merged = merge_headers([self.h1, self.h2], mode="overwrite") 

92 # The merged header should be the same type as the first header 

93 self.assertIsInstance(merged, type(self.h1)) 

94 

95 expected = { 

96 "MJD-OBS": self.h2["MJD-OBS"], 

97 "ORIGIN": self.h2["ORIGIN"], 

98 "KEY0": self.h2["KEY0"], 

99 "KEY1": self.h1["KEY1"], 

100 "KEY2": self.h2["KEY2"], 

101 "KEY3": self.h1["KEY3"], 

102 "KEY4": self.h1["KEY4"], 

103 "KEY5": self.h2["KEY5"], 

104 } 

105 self.assertEqual(merged, expected) 

106 

107 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

108 mode="overwrite") 

109 

110 expected = { 

111 "MJD-OBS": self.h4["MJD-OBS"], 

112 "ORIGIN": self.h3["ORIGIN"], 

113 "KEY0": self.h2["KEY0"], 

114 "KEY1": self.h4["KEY1"], 

115 "KEY2": self.h3["KEY2"], 

116 "KEY3": self.h3["KEY3"], 

117 "KEY4": self.h1["KEY4"], 

118 "KEY5": self.h3["KEY5"], 

119 "KEY6": self.h4["KEY6"], 

120 } 

121 

122 self.assertEqual(merged, expected) 

123 

124 def test_merging_first(self): 

125 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

126 mode="first") 

127 

128 expected = { 

129 "MJD-OBS": self.h1["MJD-OBS"], 

130 "ORIGIN": self.h1["ORIGIN"], 

131 "KEY0": self.h1["KEY0"], 

132 "KEY1": self.h1["KEY1"], 

133 "KEY2": self.h1["KEY2"], 

134 "KEY3": self.h1["KEY3"], 

135 "KEY4": self.h1["KEY4"], 

136 "KEY5": self.h2["KEY5"], 

137 "KEY6": self.h4["KEY6"], 

138 } 

139 

140 self.assertEqual(merged, expected) 

141 

142 def test_merging_drop(self): 

143 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

144 mode="drop") 

145 

146 expected = { 

147 "KEY3": self.h1["KEY3"], 

148 "KEY4": self.h1["KEY4"], 

149 "KEY5": self.h2["KEY5"], 

150 "KEY6": self.h4["KEY6"], 

151 } 

152 

153 self.assertEqual(merged, expected) 

154 

155 # Sorting the headers should make no difference to drop mode 

156 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

157 mode="drop", sort=True) 

158 self.assertEqual(merged, expected) 

159 

160 # Now retain some headers 

161 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

162 mode="drop", sort=False, first=["ORIGIN"], last=["KEY2", "KEY1"]) 

163 

164 expected = { 

165 "KEY2": self.h3["KEY2"], 

166 "ORIGIN": self.h1["ORIGIN"], 

167 "KEY1": self.h4["KEY1"], 

168 "KEY3": self.h1["KEY3"], 

169 "KEY4": self.h1["KEY4"], 

170 "KEY5": self.h2["KEY5"], 

171 "KEY6": self.h4["KEY6"], 

172 } 

173 self.assertEqual(merged, expected) 

174 

175 # Now retain some headers with sorting 

176 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

177 mode="drop", sort=True, first=["ORIGIN"], last=["KEY2", "KEY1"]) 

178 

179 expected = { 

180 "KEY2": self.h3["KEY2"], 

181 "ORIGIN": self.h2["ORIGIN"], 

182 "KEY1": self.h4["KEY1"], 

183 "KEY3": self.h1["KEY3"], 

184 "KEY4": self.h1["KEY4"], 

185 "KEY5": self.h2["KEY5"], 

186 "KEY6": self.h4["KEY6"], 

187 } 

188 self.assertEqual(merged, expected) 

189 

190 def test_merging_append(self): 

191 # Try with two headers first 

192 merged = merge_headers([self.h1, self.h2], mode="append") 

193 

194 expected = { 

195 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"]], 

196 "ORIGIN": self.h1["ORIGIN"], 

197 "KEY0": [self.h1["KEY0"], self.h2["KEY0"]], 

198 "KEY1": self.h1["KEY1"], 

199 "KEY2": [self.h1["KEY2"], self.h2["KEY2"]], 

200 "KEY3": self.h1["KEY3"], 

201 "KEY4": self.h1["KEY4"], 

202 "KEY5": self.h2["KEY5"], 

203 } 

204 

205 self.assertEqual(merged, expected) 

206 

207 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

208 mode="append") 

209 

210 expected = { 

211 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"], self.h3["MJD-OBS"], self.h4["MJD-OBS"]], 

212 "ORIGIN": [self.h1["ORIGIN"], self.h2["ORIGIN"], self.h3["ORIGIN"], None], 

213 "KEY0": [self.h1["KEY0"], self.h2["KEY0"], None, None], 

214 "KEY1": [self.h1["KEY1"], None, None, self.h4["KEY1"]], 

215 "KEY2": [self.h1["KEY2"], self.h2["KEY2"], self.h3["KEY2"], None], 

216 "KEY3": self.h3["KEY3"], 

217 "KEY4": self.h1["KEY4"], 

218 "KEY5": self.h3["KEY5"], 

219 "KEY6": self.h4["KEY6"], 

220 } 

221 

222 self.assertEqual(merged, expected) 

223 

224 def test_merging_overwrite_sort(self): 

225 merged = merge_headers([self.h1, self.h2], mode="overwrite", sort=True) 

226 

227 expected = { 

228 "MJD-OBS": self.h1["MJD-OBS"], 

229 "ORIGIN": self.h1["ORIGIN"], 

230 "KEY0": self.h1["KEY0"], 

231 "KEY1": self.h1["KEY1"], 

232 "KEY2": self.h1["KEY2"], 

233 "KEY3": self.h1["KEY3"], 

234 "KEY4": self.h1["KEY4"], 

235 "KEY5": self.h2["KEY5"], 

236 } 

237 self.assertEqual(merged, expected) 

238 

239 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

240 mode="overwrite", sort=True) 

241 

242 expected = { 

243 "MJD-OBS": self.h3["MJD-OBS"], 

244 "ORIGIN": self.h3["ORIGIN"], 

245 "KEY0": self.h1["KEY0"], 

246 "KEY1": self.h4["KEY1"], 

247 "KEY2": self.h3["KEY2"], 

248 "KEY3": self.h3["KEY3"], 

249 "KEY4": self.h1["KEY4"], 

250 "KEY5": self.h3["KEY5"], 

251 "KEY6": self.h4["KEY6"], 

252 } 

253 

254 self.assertEqual(merged, expected) 

255 

256 # Changing the order should not change the result 

257 merged = merge_headers([self.h4, self.h1, self.h3, self.h2], 

258 mode="overwrite", sort=True) 

259 

260 self.assertEqual(merged, expected) 

261 

262 def test_merging_first_sort(self): 

263 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

264 mode="first", sort=True) 

265 

266 expected = { 

267 "MJD-OBS": self.h2["MJD-OBS"], 

268 "ORIGIN": self.h2["ORIGIN"], 

269 "KEY0": self.h2["KEY0"], 

270 "KEY1": self.h1["KEY1"], 

271 "KEY2": self.h2["KEY2"], 

272 "KEY3": self.h1["KEY3"], 

273 "KEY4": self.h1["KEY4"], 

274 "KEY5": self.h2["KEY5"], 

275 "KEY6": self.h4["KEY6"], 

276 } 

277 

278 self.assertEqual(merged, expected) 

279 

280 def test_merging_append_sort(self): 

281 # Try with two headers first 

282 merged = merge_headers([self.h1, self.h2], mode="append", sort=True) 

283 

284 expected = { 

285 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"]], 

286 "ORIGIN": self.h1["ORIGIN"], 

287 "KEY0": [self.h2["KEY0"], self.h1["KEY0"]], 

288 "KEY1": self.h1["KEY1"], 

289 "KEY2": [self.h2["KEY2"], self.h1["KEY2"]], 

290 "KEY3": self.h1["KEY3"], 

291 "KEY4": self.h1["KEY4"], 

292 "KEY5": self.h2["KEY5"], 

293 } 

294 

295 self.assertEqual(merged, expected) 

296 

297 merged = merge_headers([self.h1, self.h2, self.h3, self.h4], 

298 mode="append", sort=True) 

299 

300 expected = { 

301 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"], self.h4["MJD-OBS"], self.h3["MJD-OBS"]], 

302 "ORIGIN": [self.h2["ORIGIN"], self.h1["ORIGIN"], None, self.h3["ORIGIN"]], 

303 "KEY0": [self.h2["KEY0"], self.h1["KEY0"], None, None], 

304 "KEY1": [None, self.h1["KEY1"], self.h4["KEY1"], None], 

305 "KEY2": [self.h2["KEY2"], self.h1["KEY2"], None, self.h3["KEY2"]], 

306 "KEY3": self.h3["KEY3"], 

307 "KEY4": self.h1["KEY4"], 

308 "KEY5": self.h3["KEY5"], 

309 "KEY6": self.h4["KEY6"], 

310 } 

311 

312 self.assertEqual(merged, expected) 

313 

314 # Order should not matter 

315 merged = merge_headers([self.h4, self.h3, self.h2, self.h1], 

316 mode="append", sort=True) 

317 self.assertEqual(merged, expected) 

318 

319 

320class FixHeadersTestCase(unittest.TestCase): 

321 

322 def test_basic_fix_header(self): 

323 """Test that a header can be fixed if we specify a local path. 

324 """ 

325 

326 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data")) 

327 self.assertEqual(header["DETECTOR"], "S3-111_107419-8-3") 

328 

329 # First fix header but using no search path (should work as no-op) 

330 fixed = fix_header(header) 

331 self.assertFalse(fixed) 

332 

333 # Now using the test corrections directory 

334 fixed = fix_header(header, search_path=os.path.join(TESTDIR, "data", "corrections")) 

335 self.assertTrue(fixed) 

336 self.assertEqual(header["DETECTOR"], "NEW-ID") 

337 

338 # Now with a corrections directory that has bad YAML in it 

339 with self.assertLogs(level="WARN"): 

340 fixed = fix_header(header, search_path=os.path.join(TESTDIR, "data", "bad_corrections")) 

341 self.assertFalse(fixed) 

342 

343 # Test that fix_header of unknown header is allowed 

344 header = {"SOMETHING": "UNKNOWN"} 

345 fixed = fix_header(header) 

346 self.assertFalse(fixed) 

347 

348 def test_hsc_fix_header(self): 

349 """Check that one of the known HSC corrections is being applied 

350 properly.""" 

351 header = {"EXP-ID": "HSCA00120800", 

352 "INSTRUME": "HSC", 

353 "DATA-TYP": "FLAT"} 

354 

355 fixed = fix_header(header, translator_class=HscTranslator) 

356 self.assertTrue(fixed) 

357 self.assertEqual(header["DATA-TYP"], "OBJECT") 

358 

359 # And that this header won't be corrected 

360 header = {"EXP-ID": "HSCA00120800X", 

361 "INSTRUME": "HSC", 

362 "DATA-TYP": "FLAT"} 

363 

364 fixed = fix_header(header, translator_class=HscTranslator) 

365 self.assertFalse(fixed) 

366 self.assertEqual(header["DATA-TYP"], "FLAT") 

367 

368 def test_translator_fix_header(self): 

369 """Check that translator classes can fix headers.""" 

370 

371 # Read in a known header 

372 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data")) 

373 self.assertEqual(header["DTSITE"], "ct") 

374 fixed = fix_header(header, translator_class=NotDecamTranslator) 

375 self.assertTrue(fixed) 

376 self.assertEqual(header["DTSITE"], "hi") 

377 

378 header["DTSITE"] = "reset" 

379 with self.assertLogs("astro_metadata_translator", level="FATAL"): 

380 fixed = fix_header(header, translator_class=AlsoNotDecamTranslator) 

381 self.assertFalse(fixed) 

382 self.assertEqual(header["DTSITE"], "reset") 

383 

384 

385if __name__ == "__main__": 385 ↛ 386line 385 didn't jump to line 386, because the condition on line 385 was never true

386 unittest.main()