Coverage for python / lsst / verify / tasks / apdbMetricTask.py: 57%

26 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-07 08:22 +0000

1# This file is part of verify. 

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__all__ = ["ApdbMetricTask", "ApdbMetricConfig", "ApdbMetricConnections"] 

23 

24import abc 

25 

26from lsst.pex.config import Field 

27from lsst.pipe.base import NoWorkFound, Struct, connectionTypes 

28from lsst.dax.apdb import Apdb 

29 

30from lsst.verify.tasks import MetricTask, MetricConfig, MetricConnections 

31 

32 

33class ApdbMetricConnections( 

34 MetricConnections, 

35 dimensions={"instrument"}, 

36): 

37 """An abstract connections class defining a database input. 

38 

39 Notes 

40 ----- 

41 ``ApdbMetricConnections`` defines the following dataset templates: 

42 ``package`` 

43 Name of the metric's namespace. By 

44 :ref:`verify_metrics <verify-metrics-package>` convention, this is 

45 the name of the package the metric is most closely 

46 associated with. 

47 ``metric`` 

48 Name of the metric, excluding any namespace. 

49 """ 

50 dbInfo = connectionTypes.Input( 

51 name="apdb_marker", 

52 doc="The dataset(s) indicating that AP processing has finished for a " 

53 "given data ID.", 

54 storageClass="Config", 

55 multiple=True, 

56 minimum=1, 

57 dimensions={"instrument", "visit", "detector"}, 

58 ) 

59 # Replaces MetricConnections.measurement, which is detector-level 

60 measurement = connectionTypes.Output( 

61 name="metricvalue_{package}_{metric}", 

62 doc="The metric value computed by this task.", 

63 storageClass="MetricValue", 

64 dimensions={"instrument"}, 

65 ) 

66 

67 

68class ApdbMetricConfig(MetricConfig, 

69 pipelineConnections=ApdbMetricConnections): 

70 """A base class for APDB metric task configs. 

71 """ 

72 apdb_config_url = Field( 

73 dtype=str, 

74 default=None, 

75 optional=False, 

76 doc="A config file specifying the APDB and its connection parameters, " 

77 "typically written by the apdb-cli command-line utility.", 

78 ) 

79 

80 

81class ApdbMetricTask(MetricTask): 

82 """A base class for tasks that compute metrics from an alert production 

83 database. 

84 

85 Parameters 

86 ---------- 

87 **kwargs 

88 Constructor parameters are the same as for 

89 `lsst.pipe.base.PipelineTask`. 

90 

91 Notes 

92 ----- 

93 This class should be customized by overriding `makeMeasurement`. You 

94 should not need to override `run`. 

95 """ 

96 ConfigClass = ApdbMetricConfig 

97 

98 @abc.abstractmethod 

99 def makeMeasurement(self, dbHandle, outputDataId): 

100 """Compute the metric from database data. 

101 

102 Parameters 

103 ---------- 

104 dbHandle : `lsst.dax.apdb.Apdb` 

105 A database instance. 

106 outputDataId : any data ID type 

107 The subset of the database to which this measurement applies. 

108 May be empty to represent the entire dataset. 

109 

110 Returns 

111 ------- 

112 measurement : `lsst.verify.Measurement` or `None` 

113 The measurement corresponding to the input data. 

114 

115 Raises 

116 ------ 

117 lsst.verify.tasks.MetricComputationError 

118 Raised if an algorithmic or system error prevents calculation of 

119 the metric. See `run` for expected behavior. 

120 lsst.pipe.base.NoWorkFound 

121 Raised if the metric is ill-defined or otherwise inapplicable to 

122 the database state. Typically this means that the pipeline step or 

123 option being measured was not run. 

124 """ 

125 

126 def run(self, dbInfo, outputDataId={}): 

127 """Compute a measurement from a database. 

128 

129 Parameters 

130 ---------- 

131 dbInfo : `list` 

132 The datasets (of the type indicated by the config) from 

133 which to load the database. If more than one dataset is provided 

134 (as may be the case if DB writes are fine-grained), all are 

135 assumed identical. 

136 outputDataId: any data ID type, optional 

137 The output data ID for the metric value. Defaults to the empty ID, 

138 representing a value that covers the entire dataset. 

139 

140 Returns 

141 ------- 

142 result : `lsst.pipe.base.Struct` 

143 Result struct with component: 

144 

145 ``measurement`` 

146 the value of the metric (`lsst.verify.Measurement` or `None`) 

147 

148 Raises 

149 ------ 

150 lsst.verify.tasks.MetricComputationError 

151 Raised if an algorithmic or system error prevents calculation of 

152 the metric. 

153 lsst.pipe.base.NoWorkFound 

154 Raised if the metric is ill-defined or otherwise inapplicable to 

155 the database state. Typically this means that the pipeline step or 

156 option being measured was not run. 

157 

158 Notes 

159 ----- 

160 This implementation calls 

161 `~lsst.verify.tasks.ApdbMetricConfig.dbLoader` to acquire a database 

162 handle, then passes it and the value of 

163 ``outputDataId`` to `makeMeasurement`. The result of `makeMeasurement` 

164 is returned to the caller. 

165 """ 

166 db = Apdb.from_uri(self.config.apdb_config_url) 

167 

168 if db is not None: 

169 return Struct(measurement=self.makeMeasurement(db, outputDataId)) 

170 else: 

171 raise NoWorkFound("No APDB to measure!") 

172 

173 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

174 """Do Butler I/O to provide in-memory objects for run. 

175 

176 This specialization of runQuantum passes the output data ID to `run`. 

177 """ 

178 inputs = butlerQC.get(inputRefs) 

179 outputs = self.run(**inputs, 

180 outputDataId=outputRefs.measurement.dataId) 

181 if outputs.measurement is not None: 

182 butlerQC.put(outputs, outputRefs) 

183 else: 

184 self.log.debug("Skipping measurement of %r on %s " 

185 "as not applicable.", self, inputRefs)