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

73 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-14 23:50 +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@options_file_option() 

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

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

89 

90 The calibration collection containing the camera geometry can not 

91 be specified. 

92 """ 

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

94 

95 

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

97@repo_argument(required=True) 

98@locations_argument( 

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

100) 

101@regex_option( 

102 default=fits_re, 

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

104 "Searches for fits files by default.", 

105) 

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

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

108@run_option(required=False) 

109@transfer_option() 

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

111@click.option( 

112 "--ingest-task", 

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

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

115) 

116@click.option( 

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

118 default=True, 

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

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

121 " depends on the specific datastore implentation.", 

122) 

123@failfast_option() 

124@click.option( 

125 "--update-records/--no-update-records", 

126 default=False, 

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

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

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

130) 

131@click.option( 

132 "--skip-existing/--no-skip-existing", 

133 default=True, 

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

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

136) 

137@click.option( 

138 "--search-indexes/--no-search-indexes", 

139 default=True, 

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

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

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

143) 

144@options_file_option() 

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

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

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

148 

149 

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

151@repo_argument(required=True) 

152@instrument_argument(required=True) 

153@labels_argument() 

154@click.option( 

155 "--collection", 

156 required=False, 

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

158) 

159@click.option( 

160 "--label", 

161 "labels", 

162 multiple=True, 

163 help=( 

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

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

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

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

168 "those provided by this option." 

169 ), 

170) 

171@click.option( 

172 "--prefix", 

173 required=False, 

174 help=( 

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

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

177 ), 

178) 

179@options_file_option() 

180def write_curated_calibrations( 

181 *, 

182 repo: str, 

183 instrument: str, 

184 collection: str, 

185 labels: list[str], 

186 labels_arg: list[str], 

187 prefix: str, 

188) -> None: 

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

190 script.writeCuratedCalibrations( 

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

192 ) 

193 

194 

195@click.command() 

196@repo_argument(required=True) 

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

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

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

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

201@where_option() 

202def update_dimension_regions( 

203 *, 

204 repo: str, 

205 instrument: str, 

206 collections: Sequence[str], 

207 dataset_type: str, 

208 batch_size: int, 

209 where: str, 

210) -> None: 

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

212 from ...visit_geometry import VisitGeometry 

213 

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

215 VisitGeometry.update_dimension_records( 

216 butler, instrument, where, dataset_type=dataset_type, batch_size=batch_size 

217 ) 

218 

219 

220@click.command 

221@repo_argument(required=True) 

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

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

224@where_option( 

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

226) 

227@failfast_option() 

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

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

230@processes_option() 

231@options_file_option() 

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

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

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