Coverage for python/lsst/analysis/tools/bin/verifyToSasquatch.py: 28%
51 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-15 10:10 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-15 10:10 +0000
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/>.
22__all__ = [
23 "main",
24]
26import argparse
27import datetime
28import logging
29from collections import defaultdict
30from collections.abc import Iterable, Mapping
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
37logging.basicConfig()
38_LOG = logging.getLogger(__name__)
39_LOG.setLevel(logging.INFO)
42_BASE_URL = "https://usdf-rsp-dev.slac.stanford.edu/sasquatch-rest-proxy/"
45def makeParser():
46 parser = argparse.ArgumentParser(
47 description="""Upload Measurement datasets from a Butler repository to Sasquatch.
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 )
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.")
97 return parser
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.
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.
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.")
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
144 bundle = bundles[(ref.run, ref.datasetType.name, ref.dataId)]
145 bundle.setdefault(actionId, []).append(value)
146 return bundles
149def main():
150 args = makeParser().parse_args()
151 if args.test:
152 args.namespace = "lsst.debug"
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)
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 )