Coverage for python / lsst / obs / base / cli / cmd / commands.py: 91%

75 statements  

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

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# (http://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/>. 

21 

22from collections.abc import Sequence 

23from typing import Any 

24 

25import click 

26 

27from lsst.daf.butler import Butler 

28from lsst.daf.butler.cli.opt import ( 

29 collections_argument, 

30 config_file_option, 

31 config_option, 

32 locations_argument, 

33 options_file_option, 

34 processes_option, 

35 regex_option, 

36 repo_argument, 

37 run_option, 

38 transfer_option, 

39 where_option, 

40) 

41from lsst.daf.butler.cli.utils import ButlerCommand, split_commas, typeStrAcceptsMultiple 

42from lsst.pipe.base.cli.opt import instrument_argument 

43 

44from ... import script 

45from ..opt import failfast_option, labels_argument 

46 

47# regular expression that can be used to find supported fits file extensions. 

48fits_re = r"\.fit[s]?\b" 

49 

50 

51@click.command(short_help="Define visits from exposures.", cls=ButlerCommand) 

52@repo_argument(required=True) 

53@instrument_argument(required=True) 

54@config_file_option( 

55 help="Path to a pex_config override to be included after the Instrument config overrides are applied." 

56) 

57@click.option( 

58 "--collections", 

59 help="The collections to be searched (in order) for camera geometry.", 

60 multiple=True, 

61 callback=split_commas, 

62 metavar=typeStrAcceptsMultiple, 

63) 

64@where_option() 

65@click.option( 

66 "--update-records/--no-update-records", 

67 default=False, 

68 help="Use this option to force updates to the visit definition record. " 

69 "Should only be used if you know that there has been a change to the " 

70 "exposure records, such as a change to the metadata translator.", 

71) 

72@click.option( 

73 "--incremental/--no-incremental", 

74 default=False, 

75 help="Use this option to force updates to the visit definition record " 

76 "when multi-snap visits are being ingested incrementally and so you " 

77 "might encounter partial visits. Implies --update-records.", 

78) 

79@click.option( 

80 "--skip-conflicting/--no-skip-conflicting", 

81 default=False, 

82 help="Use this option to skip over any visits that are currently defined " 

83 "with a different value and you are not sure whether to accept the new " 

84 "value. Ignored if incremental or update mode is enabled.", 

85) 

86@click.option( 

87 "--prefilter/--no-prefilter", 

88 default=False, 

89 help=( 

90 "Whether to filter out exposures that are already associated with a visit before computing " 

91 "the visits they would be associated with now. This is incompatible with --update-records and " 

92 "--incremental." 

93 ), 

94) 

95@click.option( 

96 "--check-detector-regions/--no-check-detector-regions", 

97 default=False, 

98 help=( 

99 "Whether to check that already-defined visits have the full set of detector regions defined, " 

100 "in order to insert missing ones." 

101 ), 

102) 

103@options_file_option() 

104def define_visits(*args: Any, **kwargs: Any) -> None: 

105 """Define visits from exposures in the butler registry. 

106 

107 The calibration collection containing the camera geometry can not 

108 be specified. 

109 """ 

110 script.defineVisits(*args, **kwargs) 

111 

112 

113@click.command(short_help="Ingest raw frames.", cls=ButlerCommand) 

114@repo_argument(required=True) 

115@locations_argument( 

116 help="LOCATIONS specifies files to ingest and/or locations to search for files.", required=True 

117) 

118@regex_option( 

119 default=fits_re, 

120 help="Regex string used to find files in directories listed in LOCATIONS. " 

121 "Searches for fits files by default.", 

122) 

123@config_option(metavar="TEXT=TEXT", multiple=True) 

124@config_file_option(type=click.Path(exists=True, writable=False, file_okay=True, dir_okay=False)) 

125@run_option(required=False) 

126@transfer_option() 

127@processes_option(help="Number of workers to use.") 

128@click.option( 

129 "--ingest-task", 

130 default="lsst.obs.base.RawIngestTask", 

131 help="The fully qualified class name of the ingest task to use.", 

132) 

133@click.option( 

134 "--track-file-attrs/--no-track-file-attrs", 

135 default=True, 

136 help="Indicate to the datastore whether file attributes such as file size" 

137 " or checksum should be tracked or not. Whether this parameter is honored" 

138 " depends on the specific datastore implentation.", 

139) 

140@failfast_option() 

141@click.option( 

142 "--update-records/--no-update-records", 

143 default=False, 

144 help="Use this option to force updates to the exposure records. " 

145 "Should only be used if you know that there has been a change to the " 

146 "exposure records, such as a change to the metadata translator.", 

147) 

148@click.option( 

149 "--skip-existing/--no-skip-existing", 

150 default=True, 

151 help="Use this option to control whether a raw file already existing " 

152 "in the Butler repository is an error or not.", 

153) 

154@click.option( 

155 "--search-indexes/--no-search-indexes", 

156 default=True, 

157 help="By default ingest searches for index files in each directory being " 

158 "searched. Turning this off can improve performance if you know for a fact " 

159 "you have no indexes and you are using an object store.", 

160) 

161@options_file_option() 

162def ingest_raws(*args: Any, **kwargs: Any) -> None: 

163 """Ingest raw frames into from a directory into the butler registry.""" 

164 script.ingestRaws(*args, **kwargs) 

165 

166 

167@click.command(short_help="Add an instrument's curated calibrations.", cls=ButlerCommand) 

168@repo_argument(required=True) 

169@instrument_argument(required=True) 

170@labels_argument() 

171@click.option( 

172 "--collection", 

173 required=False, 

174 help="Name of the calibration collection that associates datasets with validity ranges.", 

175) 

176@click.option( 

177 "--label", 

178 "labels", 

179 multiple=True, 

180 help=( 

181 "Extra strings to include (with automatic delimiters) in all RUN collection names, " 

182 "as well as the calibration collection name if it is not provided via --collection. " 

183 "May be provided as a positional argument instead of or in addition to this option, " 

184 "as long as at least one label is provided. Positional-argument labels appear before " 

185 "those provided by this option." 

186 ), 

187) 

188@click.option( 

189 "--prefix", 

190 required=False, 

191 help=( 

192 "Prefix for the collection name. Default is the instrument name. " 

193 "This is ignored if --collection is passed." 

194 ), 

195) 

196@options_file_option() 

197def write_curated_calibrations( 

198 *, 

199 repo: str, 

200 instrument: str, 

201 collection: str, 

202 labels: list[str], 

203 labels_arg: list[str], 

204 prefix: str, 

205) -> None: 

206 """Add an instrument's curated calibrations to the data repository.""" 

207 script.writeCuratedCalibrations( 

208 repo=repo, instrument=instrument, collection=collection, labels=labels_arg + labels, prefix=prefix 

209 ) 

210 

211 

212@click.command() 

213@repo_argument(required=True) 

214@instrument_argument(required=True, help="Name of the instrument to use for updates.") 

215@collections_argument(help="Collections to search for visit geometry datasets.") 

216@click.option("--dataset-type", default="visit_geometry", help="Name of the visit geometry dataset type.") 

217@click.option("--batch-size", default=11, help="Number of visits to process in each transaction.") 

218@where_option() 

219def update_dimension_regions( 

220 *, 

221 repo: str, 

222 instrument: str, 

223 collections: Sequence[str], 

224 dataset_type: str, 

225 batch_size: int, 

226 where: str, 

227) -> None: 

228 """Update dimension record regions from visit geometry datasets.""" 

229 from ...visit_geometry import VisitGeometry 

230 

231 with Butler.from_config(repo, writeable=True, collections=collections) as butler: 

232 VisitGeometry.update_dimension_records( 

233 butler, instrument, where, dataset_type=dataset_type, batch_size=batch_size 

234 ) 

235 

236 

237@click.command 

238@repo_argument(required=True) 

239@instrument_argument(required=True, help="Instrument to use for finding raw datasets.") 

240@collections_argument(help="List of collections to search for raws instead of the default.") 

241@where_option( 

242 help="A string expression to use to query the butler to find raw datasets to use for record updates." 

243) 

244@failfast_option() 

245@config_option(metavar="TEXT=TEXT", multiple=True) 

246@config_file_option(type=click.Path(exists=True, writable=False, file_okay=True, dir_okay=False)) 

247@processes_option() 

248@options_file_option() 

249def update_exposures_from_raws(*args: Any, **kwargs: Any) -> None: 

250 """Update exposure records associated with specific raw datasets.""" 

251 script.updateExposures(*args, **kwargs)