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 NullDecamTranslator(DecamTranslator): 

43 """This is a DECam translator that doesn't do any fixes.""" 

44 name = None 

45 

46 @classmethod 

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

48 return False 

49 

50 

51class HeadersTestCase(unittest.TestCase): 

52 

53 def setUp(self): 

54 # Define reference headers 

55 self.h1 = dict( 

56 ORIGIN="LSST", 

57 KEY0=0, 

58 KEY1=1, 

59 KEY2=3, 

60 KEY3=3.1415, 

61 KEY4="a", 

62 ) 

63 

64 self.h2 = dict( 

65 ORIGIN="LSST", 

66 KEY0="0", 

67 KEY2=4, 

68 KEY5=42 

69 ) 

70 self.h3 = dict( 

71 ORIGIN="AUXTEL", 

72 KEY3=3.1415, 

73 KEY2=50, 

74 KEY5=42, 

75 ) 

76 self.h4 = dict( 

77 KEY6="New", 

78 KEY1="Exists", 

79 ) 

80 

81 # Add keys for sorting by time 

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

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

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

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

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

87 

88 def test_fail(self): 

89 with self.assertRaises(ValueError): 

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

91 

92 with self.assertRaises(ValueError): 

93 merge_headers([]) 

94 

95 def test_one(self): 

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

97 self.assertEqual(merged, self.h1) 

98 

99 def test_merging_overwrite(self): 

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

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

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

103 

104 expected = { 

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

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

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

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

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

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

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

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

113 } 

114 self.assertEqual(merged, expected) 

115 

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

117 mode="overwrite") 

118 

119 expected = { 

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

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

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

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

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

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

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

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

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

129 } 

130 

131 self.assertEqual(merged, expected) 

132 

133 def test_merging_first(self): 

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

135 mode="first") 

136 

137 expected = { 

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

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

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

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

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

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

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

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

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

147 } 

148 

149 self.assertEqual(merged, expected) 

150 

151 def test_merging_drop(self): 

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

153 mode="drop") 

154 

155 expected = { 

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

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

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

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

160 } 

161 

162 self.assertEqual(merged, expected) 

163 

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

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

166 mode="drop", sort=True) 

167 self.assertEqual(merged, expected) 

168 

169 # Now retain some headers 

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

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

172 

173 expected = { 

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

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

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

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

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

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

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

181 } 

182 self.assertEqual(merged, expected) 

183 

184 # Now retain some headers with sorting 

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

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

187 

188 expected = { 

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

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

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

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

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

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

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

196 } 

197 self.assertEqual(merged, expected) 

198 

199 def test_merging_append(self): 

200 # Try with two headers first 

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

202 

203 expected = { 

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

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

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

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

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

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

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

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

212 } 

213 

214 self.assertEqual(merged, expected) 

215 

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

217 mode="append") 

218 

219 expected = { 

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

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

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

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

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

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

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

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

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

229 } 

230 

231 self.assertEqual(merged, expected) 

232 

233 def test_merging_overwrite_sort(self): 

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

235 

236 expected = { 

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

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

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

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

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

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

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

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

245 } 

246 self.assertEqual(merged, expected) 

247 

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

249 mode="overwrite", sort=True) 

250 

251 expected = { 

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

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

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

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

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

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

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

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

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

261 } 

262 

263 self.assertEqual(merged, expected) 

264 

265 # Changing the order should not change the result 

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

267 mode="overwrite", sort=True) 

268 

269 self.assertEqual(merged, expected) 

270 

271 def test_merging_first_sort(self): 

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

273 mode="first", sort=True) 

274 

275 expected = { 

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

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

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

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

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

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

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

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

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

285 } 

286 

287 self.assertEqual(merged, expected) 

288 

289 def test_merging_append_sort(self): 

290 # Try with two headers first 

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

292 

293 expected = { 

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

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

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

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

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

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

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

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

302 } 

303 

304 self.assertEqual(merged, expected) 

305 

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

307 mode="append", sort=True) 

308 

309 expected = { 

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

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

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

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

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

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

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

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

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

319 } 

320 

321 self.assertEqual(merged, expected) 

322 

323 # Order should not matter 

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

325 mode="append", sort=True) 

326 self.assertEqual(merged, expected) 

327 

328 

329class FixHeadersTestCase(unittest.TestCase): 

330 

331 def test_basic_fix_header(self): 

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

333 """ 

334 

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

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

337 

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

339 fixed = fix_header(header, translator_class=NullDecamTranslator) 

340 self.assertFalse(fixed) 

341 

342 # Now using the test corrections directory 

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

344 translator_class=NullDecamTranslator) 

345 self.assertTrue(fixed) 

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

347 

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

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

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

351 translator_class=NullDecamTranslator) 

352 self.assertFalse(fixed) 

353 

354 # Test that fix_header of unknown header is allowed 

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

356 fixed = fix_header(header, translator_class=NullDecamTranslator) 

357 self.assertFalse(fixed) 

358 

359 def test_hsc_fix_header(self): 

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

361 properly.""" 

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

363 "INSTRUME": "HSC", 

364 "DATA-TYP": "FLAT"} 

365 

366 fixed = fix_header(header, translator_class=HscTranslator) 

367 self.assertTrue(fixed) 

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

369 

370 # And that this header won't be corrected 

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

372 "INSTRUME": "HSC", 

373 "DATA-TYP": "FLAT"} 

374 

375 fixed = fix_header(header, translator_class=HscTranslator) 

376 self.assertFalse(fixed) 

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

378 

379 def test_decam_fix_header(self): 

380 """Check that one of the known DECam corrections is being applied 

381 properly.""" 

382 

383 # This header is a bias (zero) with an erroneous Y filter 

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

385 fixed = fix_header(header, translator_class=DecamTranslator) 

386 self.assertTrue(fixed) 

387 self.assertEqual(header["FILTER"], "solid plate 0.0 0.0") 

388 

389 def test_translator_fix_header(self): 

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

391 

392 # Read in a known header 

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

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

395 fixed = fix_header(header, translator_class=NotDecamTranslator) 

396 self.assertTrue(fixed) 

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

398 

399 header["DTSITE"] = "reset" 

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

401 fixed = fix_header(header, translator_class=AlsoNotDecamTranslator) 

402 self.assertFalse(fixed) 

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

404 

405 

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

407 unittest.main()