12 """Read data for a particular sensor from the standard format at a particular root.
17 Path to the top level of the data tree. This is expected to hold directories
18 named after the sensor names. They are expected to be lower case.
20 The name of the sensor for which to read data.
22 The identifier for the sensor in question.
27 A dictionary of objects constructed from the appropriate factory class.
28 The key is the validity start time as a `datetime` object.
30 factory_map = {
'qe_curve': Curve,
'defects': Defects,
'linearizer': Linearizer,
31 'crosstalk': CrosstalkCalib}
33 extensions = (
".ecsv",
".yaml")
34 for ext
in extensions:
35 files.extend(glob.glob(os.path.join(root, chip_name, f
"*{ext}")))
36 parts = os.path.split(root)
37 instrument = os.path.split(parts[0])[1]
39 if data_name
not in factory_map:
40 raise ValueError(f
"Unknown calibration data type, '{data_name}' found. "
41 f
"Only understand {','.join(k for k in factory_map)}")
42 factory = factory_map[data_name]
45 date_str = os.path.splitext(os.path.basename(f))[0]
46 valid_start = dateutil.parser.parse(date_str)
47 data_dict[valid_start] = factory.readText(f)
48 check_metadata(data_dict[valid_start], valid_start, instrument, chip_id, f, data_name)
49 return data_dict, data_name
52 def check_metadata(obj, valid_start, instrument, chip_id, filepath, data_name):
53 """Check that the metadata is complete and self consistent
57 obj : object of same type as the factory
58 Object to retrieve metadata from in order to compare with
59 metadata inferred from the path.
60 valid_start : `datetime`
61 Start of the validity range for data
63 Name of the instrument in question
65 Identifier of the sensor in question
67 Path of the file read to construct the data
69 Name of the type of data being read
78 If the metadata from the path and the metadata encoded
79 in the path do not match for any reason.
81 md = obj.getMetadata()
82 finst = md[
'INSTRUME']
83 fchip_id = md[
'DETECTOR']
84 fdata_name = md[
'OBSTYPE']
85 if not ((finst.lower(), int(fchip_id), fdata_name.lower())
86 == (instrument.lower(), chip_id, data_name.lower())):
87 raise ValueError(f
"Path and file metadata do not agree:\n"
88 f
"Path metadata: {instrument} {chip_id} {data_name}\n"
89 f
"File metadata: {finst} {fchip_id} {fdata_name}\n"
90 f
"File read from : %s\n"%(filepath)
95 """Read all data from the standard format at a particular root.
100 Path to the top level of the data tree. This is expected to hold directories
101 named after the sensor names. They are expected to be lower case.
102 camera : `lsst.afw.cameraGeom.Camera`
103 The camera that goes with the data being read.
108 A dictionary of dictionaries of objects constructed with the appropriate factory class.
109 The first key is the sensor name lowered, and the second is the validity
110 start time as a `datetime` object.
114 Each leaf object in the constructed dictionary has metadata associated with it.
115 The detector ID may be retrieved from the DETECTOR entry of that metadata.
117 root = os.path.normpath(root)
118 dirs = os.listdir(root)
119 dirs = [d
for d
in dirs
if os.path.isdir(os.path.join(root, d))]
121 name_map = {det.getName().lower(): det.getName()
for
125 raise RuntimeError(f
"No data found on path {root}")
129 chip_name = os.path.basename(d)
130 chip_id = camera[name_map[chip_name]].getId()
131 data_by_chip[chip_name], calib_type =
read_one_chip(root, chip_name, chip_id)
132 calib_types.add(calib_type)
133 if len(calib_types) != 1:
134 raise ValueError(f
'Error mixing calib types: {calib_types}')
136 no_data = all([v == {}
for v
in data_by_chip.values()])
138 raise RuntimeError(f
'No data to ingest')
140 return data_by_chip, calib_type