Coverage for python/lsst/obs/base/gen2to3/calibRepoConverter.py : 24%

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 obs_base.
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 <http://www.gnu.org/licenses/>.
21from __future__ import annotations
23__all__ = ["CalibRepoConverter"]
25import os
26import sqlite3
27import astropy.time
28from typing import TYPE_CHECKING, Dict, Iterator, Tuple, Optional
30from .repoConverter import RepoConverter
31from .repoWalker import RepoWalker
32from .translators import makeCalibrationLabel
34if TYPE_CHECKING: 34 ↛ 35line 34 didn't jump to line 35, because the condition on line 34 was never true
35 from lsst.daf.butler import StorageClass, FormatterParameter
36 from .repoWalker.scanner import PathElementHandler
37 from ..cameraMapper import CameraMapper
38 from ..mapping import Mapping as CameraMapperMapping # disambiguate from collections.abc.Mapping
41class CalibRepoConverter(RepoConverter):
42 """A specialization of `RepoConverter` for calibration repositories.
44 Parameters
45 ----------
46 mapper : `CameraMapper`
47 Gen2 mapper for the data repository. The root associated with the
48 mapper is ignored and need not match the root of the repository.
49 kwds
50 Additional keyword arguments are forwarded to (and required by)
51 `RepoConverter`.
52 """
54 def __init__(self, *, mapper: CameraMapper, **kwds):
55 super().__init__(**kwds)
56 self.mapper = mapper
57 self._datasetTypes = set()
59 def isDatasetTypeSpecial(self, datasetTypeName: str) -> bool:
60 # Docstring inherited from RepoConverter.
61 return datasetTypeName in self.task.config.curatedCalibrations
63 def iterMappings(self) -> Iterator[Tuple[str, CameraMapperMapping]]:
64 # Docstring inherited from RepoConverter.
65 yield from self.mapper.calibrations.items()
67 def makeRepoWalkerTarget(self, datasetTypeName: str, template: str, keys: Dict[str, type],
68 storageClass: StorageClass, formatter: FormatterParameter = None,
69 targetHandler: Optional[PathElementHandler] = None,
70 ) -> RepoWalker.Target:
71 # Docstring inherited from RepoConverter.
72 target = RepoWalker.Target(
73 datasetTypeName=datasetTypeName,
74 storageClass=storageClass,
75 template=template,
76 keys=keys,
77 instrument=self.task.instrument.getName(),
78 universe=self.task.registry.dimensions,
79 formatter=formatter,
80 targetHandler=targetHandler,
81 translatorFactory=self.task.translatorFactory,
82 )
83 self._datasetTypes.add(target.datasetType)
84 return target
86 def insertDimensionData(self):
87 # Docstring inherited from RepoConverter.
88 # This has only been tested on HSC, and it's not clear how general it
89 # is. The catch is that it needs to generate calibration_label strings
90 # consistent with those produced by the Translator system.
91 db = sqlite3.connect(os.path.join(self.root, "calibRegistry.sqlite3"))
92 db.row_factory = sqlite3.Row
93 records = []
94 for datasetType in self._datasetTypes:
95 if "calibration_label" not in datasetType.dimensions:
96 continue
97 fields = ["validStart", "validEnd", "calibDate"]
98 if "detector" in datasetType.dimensions.names:
99 fields.append(self.task.config.ccdKey)
100 else:
101 fields.append(f"NULL AS {self.task.config.ccdKey}")
102 if ("physical_filter" in datasetType.dimensions.names
103 or "abstract_filter" in datasetType.dimensions.names):
104 fields.append("filter")
105 else:
106 fields.append("NULL AS filter")
107 query = f"SELECT DISTINCT {', '.join(fields)} FROM {datasetType.name};"
108 try:
109 results = db.execute(query)
110 except sqlite3.OperationalError as e:
111 if (self.mapper.mappings[datasetType.name].tables is None
112 or len(self.mapper.mappings[datasetType.name].tables) == 0):
113 self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
114 datasetType.name, self.root, e)
115 continue
116 # Try using one of the alternate table names in the mapper (e.g. cpBias->bias for DECam).
117 name = self.mapper.mappings[datasetType.name].tables[0]
118 query = f"SELECT DISTINCT {', '.join(fields)} FROM {name};"
119 try:
120 results = db.execute(query)
121 except sqlite3.OperationalError as e:
122 self.task.log.warn("Could not extract calibration ranges for %s in %s: %r",
123 datasetType.name, self.root, e)
124 continue
125 for row in results:
126 label = makeCalibrationLabel(datasetType.name, row["calibDate"],
127 ccd=row[self.task.config.ccdKey], filter=row["filter"])
128 # For validity times we use TAI as some gen2 repos have validity
129 # dates very far in the past or future.
130 day = astropy.time.TimeDelta(1, format="jd", scale="tai")
131 records.append({
132 "instrument": self.task.instrument.getName(),
133 "name": label,
134 "datetime_begin": astropy.time.Time(row["validStart"], format="iso", scale="tai"),
135 "datetime_end": astropy.time.Time(row["validEnd"], format="iso", scale="tai") + day
136 })
137 if records:
138 self.task.registry.insertDimensionData("calibration_label", *records)
140 # Class attributes that will be shadowed by public instance attributes;
141 # defined here only for documentation purposes.
143 mapper: CameraMapper
144 """Gen2 mapper associated with this repository.
145 """