Coverage for python/lsst/analysis/tools/bin/verifyToSasquatch.py: 28%

51 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-06 04:05 -0700

1# This file is part of analysis_tools. 

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__ = [ 

23 "main", 

24] 

25 

26import argparse 

27import datetime 

28import logging 

29from collections import defaultdict 

30from collections.abc import Iterable, Mapping 

31 

32import lsst.verify 

33from lsst.analysis.tools.interfaces import MetricMeasurementBundle 

34from lsst.analysis.tools.interfaces.datastore import SasquatchDispatcher 

35from lsst.daf.butler import Butler, DataCoordinate, DatasetRef 

36 

37logging.basicConfig() 

38_LOG = logging.getLogger(__name__) 

39_LOG.setLevel(logging.INFO) 

40 

41 

42_BASE_URL = "https://usdf-rsp-dev.slac.stanford.edu/sasquatch-rest-proxy/" 

43 

44 

45def makeParser(): 

46 parser = argparse.ArgumentParser( 

47 description="""Upload Measurement datasets from a Butler repository to Sasquatch. 

48 

49 This script handles metric values persisted directly using 

50 lsst.verify tooling. It is neither necessary nor useful for 

51 MetricMeasurementBundles created using analysis_tools 

52 tooling, and is provided solely for backwards compatibility 

53 with the older system. 

54 """, 

55 add_help=True, 

56 ) 

57 parser.add_argument("repo", help="The Butler repository from which to upload metric values.") 

58 parser.add_argument( 

59 "collections", 

60 action="extend", 

61 nargs="+", 

62 help="The collection(s) in which to search for metric values. These can " 

63 "be specified in any notation recognized by Middleware.", 

64 ) 

65 parser.add_argument("--dataset", required=True, help="The dataset on which the metrics were measured.") 

66 parser.add_argument( 

67 "--test", 

68 action="store_true", 

69 help="Run this command while uploading to the lsst.debug test " 

70 "namespace. Any --namespace argument is ignored.", 

71 ) 

72 parser.add_argument( 

73 "--where", default="", help="Butler query to select metric values for upload (default: all values)." 

74 ) 

75 parser.add_argument( 

76 "--date-created", 

77 type=datetime.datetime.fromisoformat, 

78 help="ISO8601 formatted datetime in UTC for the Measurement creation " 

79 "date, e.g. 2021-06-30T22:28:25Z. If not provided, the run time or " 

80 "current time is used.", 

81 ) 

82 

83 api_group = parser.add_argument_group("Sasquatch API arguments") 

84 api_group.add_argument( 

85 "--namespace", 

86 default="lsst.dm", 

87 help="The Sasquatch namespace to which to upload the metric values (default: lsst.dm)", 

88 ) 

89 api_group.add_argument( 

90 "--url", 

91 dest="base_url", 

92 default=_BASE_URL, 

93 help=f"Root URL of Sasquatch proxy server (default: {_BASE_URL}).", 

94 ) 

95 api_group.add_argument("--token", default="na", help="Authentication token for the proxy server.") 

96 

97 return parser 

98 

99 

100def _bundle_metrics( 

101 butler: Butler, metricValues: Iterable[DatasetRef] 

102) -> Mapping[tuple[str, str, DataCoordinate], MetricMeasurementBundle]: 

103 """Organize free metric values into metric bundles while preserving as much 

104 information as practical. 

105 

106 Parameters 

107 ---------- 

108 butler : `lsst.daf.butler.Butler` 

109 The Butler repository containing the metric values. 

110 metricValues : `~collections.abc.Iterable` [`lsst.daf.butler.DatasetRef`] 

111 The datasets to bundle. All references must point to ``MetricValue`` 

112 datasets. 

113 

114 Returns 

115 ------- 

116 bundles : `~collections.abc.Mapping` 

117 A collection of 

118 `lsst.analysis.tools.interfaces.MetricMeasurementBundle`, one for each 

119 combination of distinct metadata. The mapping key is a tuple of (run, 

120 dataset type, data ID), and the value is the corresponding bundle. 

121 To simplify the uploaded schemas, the bundle uses metrics' relative 

122 (unqualified) names even if the input measurements were 

123 fully-qualified. 

124 """ 

125 bundles = defaultdict(MetricMeasurementBundle) 

126 for ref in metricValues: 

127 value = butler.get(ref) 

128 # MeasurementMetricBundle doesn't validate input. 

129 if not isinstance(value, lsst.verify.Measurement): 

130 raise ValueError(f"{ref} is not a metric value.") 

131 

132 # HACK: in general, metric names are fully qualified, and this becomes 

133 # the InfluxDB field name. lsst.verify-style metrics have unique names 

134 # already, so remove the package qualification. 

135 value = lsst.verify.Measurement( 

136 value.metric_name.metric, value.quantity, value.blobs.values(), value.extras, value.notes 

137 ) 

138 # These metrics weren't created by actions. Sasquatch requires that 

139 # each actionId produce the same metrics on every run (see 

140 # https://sasquatch.lsst.io/user-guide/avro.html), so choose something 

141 # unique to the metric. 

142 actionId = value.metric_name.metric 

143 

144 bundle = bundles[(ref.run, ref.datasetType.name, ref.dataId)] 

145 bundle.setdefault(actionId, []).append(value) 

146 return bundles 

147 

148 

149def main(): 

150 args = makeParser().parse_args() 

151 if args.test: 

152 args.namespace = "lsst.debug" 

153 

154 butler = Butler(args.repo, collections=args.collections, writeable=False) 

155 metricTypes = {t for t in butler.registry.queryDatasetTypes() if t.storageClass_name == "MetricValue"} 

156 metricValues = butler.registry.queryDatasets(metricTypes, where=args.where, findFirst=True) 

157 _LOG.info("Found %d metric values in %s.", metricValues.count(), args.collections) 

158 

159 bundles = _bundle_metrics(butler, metricValues) 

160 dispatcher = SasquatchDispatcher(url=args.base_url, token=args.token, namespace=args.namespace) 

161 _LOG.info("Uploading to %s @ %s...", args.namespace, args.base_url) 

162 for (run, datasetType, dataId), bundle in bundles.items(): 

163 dispatcher.dispatch( 

164 bundle, 

165 run=run, 

166 datasetType=datasetType, 

167 timestamp=args.date_created, 

168 datasetIdentifier=args.dataset, 

169 identifierFields=dataId, 

170 )