Coverage for python / lsst / daf / butler / registry / obscore / _config.py: 90%

88 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-14 23:37 +0000

1# This file is part of daf_butler. 

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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27 

28from __future__ import annotations 

29 

30__all__ = [ 

31 "ConfigCollectionType", 

32 "DatasetTypeConfig", 

33 "ExtraColumnConfig", 

34 "ExtraColumnType", 

35 "ObsCoreConfig", 

36 "ObsCoreManagerConfig", 

37 "SpatialPluginConfig", 

38] 

39 

40import enum 

41from typing import Any 

42 

43import pydantic 

44from pydantic import StrictBool, StrictFloat, StrictInt, StrictStr 

45 

46 

47class ExtraColumnType(str, enum.Enum): 

48 """Enum class defining possible values for types of extra columns.""" 

49 

50 bool = "bool" 

51 int = "int" 

52 float = "float" 

53 string = "string" 

54 

55 

56class ExtraColumnConfig(pydantic.BaseModel): 

57 """Configuration class describing specification of additional column in 

58 obscore table. 

59 """ 

60 

61 template: str 

62 """Template string for formatting the column value.""" 

63 

64 type: ExtraColumnType = ExtraColumnType.string 

65 """Column type, formatted string will be converted to this actual type.""" 

66 

67 length: int | None = None 

68 """Optional length qualifier for a column, only used for strings.""" 

69 

70 doc: str | None = None 

71 """Documentation string for this column.""" 

72 

73 

74class DatasetTypeConfig(pydantic.BaseModel): 

75 """Configuration describing dataset type-related options.""" 

76 

77 dataproduct_type: str 

78 """Value for the ``dataproduct_type`` column.""" 

79 

80 dataproduct_subtype: str | None = None 

81 """Value for the ``dataproduct_subtype`` column, optional.""" 

82 

83 calib_level: int 

84 """Value for the ``calib_level`` column.""" 

85 

86 o_ucd: str | None = None 

87 """Value for the ``o_ucd`` column, optional.""" 

88 

89 access_format: str | None = None 

90 """Value for the ``access_format`` column, optional.""" 

91 

92 obs_id_fmt: str | None = None 

93 """Format string for ``obs_id`` column, optional. Uses `str.format` 

94 syntax. 

95 """ 

96 

97 datalink_url_fmt: str | None = None 

98 """Format string for ``access_url`` column for DataLink.""" 

99 

100 obs_collection: str | None = None 

101 """Value for the ``obs_collection`` column, if specified it overrides 

102 global value in `ObsCoreConfig`.""" 

103 

104 s_xel: tuple[int, int] | None = None 

105 """The number of pixels in the first and second dimension of the dataset 

106 type. 

107 """ 

108 

109 extra_columns: ( 

110 None | (dict[str, StrictFloat | StrictInt | StrictBool | StrictStr | ExtraColumnConfig]) 

111 ) = None 

112 """Description for additional columns, optional. 

113 

114 Keys are the names of the columns, values can be literal constants with the 

115 values, or ExtraColumnConfig mappings.""" 

116 

117 

118class SpatialPluginConfig(pydantic.BaseModel): 

119 """Configuration class for a spatial plugin.""" 

120 

121 cls: str 

122 """Name of the class implementing plugin methods.""" 

123 

124 config: dict[str, Any] = pydantic.Field(default_factory=dict) 

125 """Configuration object passed to plugin ``initialize()`` method.""" 

126 

127 

128class ObsCoreConfig(pydantic.BaseModel): 

129 """Configuration which controls conversion of Registry datasets into 

130 obscore records. 

131 

132 This configuration is a base class for ObsCore manager configuration class. 

133 It can also be used by other tools that use `RecordFactory` to convert 

134 datasets into obscore records. 

135 """ 

136 

137 collections: list[str] | None = None 

138 """Registry collections to include, if missing then all collections are 

139 used. Depending on implementation the name in the list can be either a 

140 full collection name or a regular expression. 

141 """ 

142 

143 dataset_types: dict[str, DatasetTypeConfig] 

144 """Per-dataset type configuration, key is the dataset type name.""" 

145 

146 obs_collection: str | None = None 

147 """Value for the ``obs_collection`` column. This can be overridden in 

148 dataset type configuration. 

149 """ 

150 

151 facility_name: str 

152 """Default value for the ``facility_name`` column. If an instrument 

153 is listed in ``facility_map`` that will be used in preference but this 

154 value must always be set as a fallback.""" 

155 

156 facility_map: dict[str, str] = pydantic.Field(default_factory=dict) 

157 """Mapping of instrument name to facility name. Takes precedence over 

158 the ``facility_name``.""" 

159 

160 extra_columns: ( 

161 None | (dict[str, StrictFloat | StrictInt | StrictBool | StrictStr | ExtraColumnConfig]) 

162 ) = None 

163 """Description for additional columns, optional. 

164 

165 Keys are the names of the columns, values can be literal constants with the 

166 values, or ExtraColumnConfig mappings.""" 

167 

168 indices: dict[str, str | list[str]] | None = None 

169 """Description of indices, key is the index name, value is the list of 

170 column names or a single column name. The index name may not be used for 

171 an actual index. 

172 """ 

173 

174 spectral_ranges: dict[str, tuple[float | None, float | None]] = {} 

175 """Maps band name or filter name to a min/max of spectral range. One or 

176 both ends can be specified as `None`. 

177 """ 

178 

179 spatial_plugins: dict[str, SpatialPluginConfig] = {} 

180 """Optional configuration for plugins managing spatial columns and 

181 indices. The key is an arbitrary name and the value is an object describing 

182 plugin class and its configuration options. By default there is no spatial 

183 indexing support, but a standard ``s_region`` column is always included. 

184 """ 

185 

186 fallback_instrument: str | None = None 

187 """Instrument to use if a dataset type does not have an instrument 

188 dimension. Will be left unset if `None`. Can be dangerous to set this 

189 in a repository containing data from multiple instruments.""" 

190 

191 obs_publisher_did_fmt: str | None = None 

192 """Format string to generate an obs_publisher_did for a record. Assumes 

193 that a single string is suitable for all results. 

194 """ 

195 

196 

197class ConfigCollectionType(str, enum.Enum): 

198 """Enum class defining possible values for configuration attributes.""" 

199 

200 RUN = "RUN" 

201 TAGGED = "TAGGED" 

202 

203 

204class ObsCoreManagerConfig(ObsCoreConfig): 

205 """Complete configuration for ObsCore manager.""" 

206 

207 namespace: str = "daf_butler_obscore" 

208 """Unique namespace to distinguish different instances, used for schema 

209 migration purposes. 

210 """ 

211 

212 version: int 

213 """Version of configuration, used for schema migration purposes. It needs 

214 to be incremented on every change of configuration that causes a schema or 

215 data migration. 

216 """ 

217 

218 table_name: str = "obscore" 

219 """Name of the table for ObsCore records.""" 

220 

221 collection_type: ConfigCollectionType 

222 """Type of the collections that can appear in ``collections`` attribute. 

223 

224 When ``collection_type`` is ``RUN`` then ``collections`` contains regular 

225 expressions that will be used to match RUN collections only. When 

226 ``collection_type`` is ``TAGGED`` then ``collections`` must contain 

227 exactly one collection name which must be TAGGED collection. 

228 """ 

229 

230 @pydantic.model_validator(mode="after") 

231 def validate_collection_type(self) -> ObsCoreManagerConfig: 

232 """Check that contents of ``collections`` is consistent with 

233 ``collection_type``. 

234 """ 

235 if self.collection_type is ConfigCollectionType.TAGGED: 

236 collections: list[str] | None = self.collections 

237 if collections is None or len(collections) != 1: 

238 raise ValueError("'collections' must have one element when 'collection_type' is TAGGED") 

239 return self