Coverage for python/lsst/daf/butler/_column_tags.py: 74%

70 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-03 02:48 -0700

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 "DatasetColumnTag", 

32 "DimensionKeyColumnTag", 

33 "DimensionRecordColumnTag", 

34 "is_timespan_column", 

35) 

36 

37import dataclasses 

38from collections.abc import Iterable 

39from typing import TYPE_CHECKING, Any, TypeVar, final 

40 

41_S = TypeVar("_S") 

42 

43if TYPE_CHECKING: 

44 from lsst.daf.relation import ColumnTag 

45 

46 

47class _BaseColumnTag: 

48 __slots__ = () 

49 

50 @classmethod 

51 def filter_from(cls: type[_S], tags: Iterable[Any]) -> set[_S]: 

52 return {tag for tag in tags if type(tag) is cls} 

53 

54 

55@final 

56@dataclasses.dataclass(frozen=True, slots=True) 

57class DimensionKeyColumnTag(_BaseColumnTag): 

58 """An identifier for `~lsst.daf.relation.Relation` columns that represent 

59 a dimension primary key value. 

60 """ 

61 

62 dimension: str 

63 """Name of the dimension (`str`).""" 

64 

65 def __str__(self) -> str: 

66 return self.dimension 

67 

68 @property 

69 def qualified_name(self) -> str: 

70 return self.dimension 

71 

72 @property 

73 def is_key(self) -> bool: 

74 return True 

75 

76 @classmethod 

77 def generate(cls, dimensions: Iterable[str]) -> list[DimensionKeyColumnTag]: 

78 """Return a list of column tags from an iterable of dimension 

79 names. 

80 

81 Parameters 

82 ---------- 

83 dimensions : `~collections.abc.Iterable` [ `str` ] 

84 Dimension names. 

85 

86 Returns 

87 ------- 

88 tags : `list` [ `DimensionKeyColumnTag` ] 

89 List of column tags. 

90 """ 

91 return [cls(d) for d in dimensions] 

92 

93 

94@final 

95@dataclasses.dataclass(frozen=True, slots=True) 

96class DimensionRecordColumnTag(_BaseColumnTag): 

97 """An identifier for `~lsst.daf.relation.Relation` columns that represent 

98 non-key columns in a dimension or dimension element record. 

99 """ 

100 

101 element: str 

102 """Name of the dimension element (`str`). 

103 """ 

104 

105 column: str 

106 """Name of the column (`str`).""" 

107 

108 def __str__(self) -> str: 

109 return f"{self.element}.{self.column}" 

110 

111 @property 

112 def qualified_name(self) -> str: 

113 return f"n!{self.element}:{self.column}" 

114 

115 @property 

116 def is_key(self) -> bool: 

117 return False 

118 

119 @classmethod 

120 def generate(cls, element: str, columns: Iterable[str]) -> list[DimensionRecordColumnTag]: 

121 """Return a list of column tags from an iterable of column names 

122 for a single dimension element. 

123 

124 Parameters 

125 ---------- 

126 element : `str` 

127 Name of the dimension element. 

128 columns : `~collections.abc.Iterable` [ `str` ] 

129 Column names. 

130 

131 Returns 

132 ------- 

133 tags : `list` [ `DimensionRecordColumnTag` ] 

134 List of column tags. 

135 """ 

136 return [cls(element, column) for column in columns] 

137 

138 

139@final 

140@dataclasses.dataclass(frozen=True, slots=True) 

141class DatasetColumnTag(_BaseColumnTag): 

142 """An identifier for `~lsst.daf.relation.Relation` columns that represent 

143 columns from a dataset query or subquery. 

144 """ 

145 

146 dataset_type: str 

147 """Name of the dataset type (`str`).""" 

148 

149 column: str 

150 """Name of the column (`str`). 

151 

152 Allowed values are: 

153 

154 - "dataset_id" (autoincrement or UUID primary key) 

155 - "run" (collection primary key, not collection name) 

156 - "ingest_date" 

157 - "timespan" (validity range, or NULL for non-calibration collections) 

158 - "rank" (collection position in ordered search) 

159 """ 

160 

161 def __str__(self) -> str: 

162 return f"{self.dataset_type}.{self.column}" 

163 

164 @property 

165 def qualified_name(self) -> str: 

166 return f"t!{self.dataset_type}:{self.column}" 

167 

168 @property 

169 def is_key(self) -> bool: 

170 return self.column == "dataset_id" or self.column == "run" 

171 

172 @classmethod 

173 def generate(cls, dataset_type: str, columns: Iterable[str]) -> list[DatasetColumnTag]: 

174 """Return a list of column tags from an iterable of column names 

175 for a single dataset type. 

176 

177 Parameters 

178 ---------- 

179 dataset_type : `str` 

180 Name of the dataset type. 

181 columns : `~collections.abc.Iterable` [ `str` ] 

182 Column names. 

183 

184 Returns 

185 ------- 

186 tags : `list` [ `DatasetColumnTag` ] 

187 List of column tags. 

188 """ 

189 return [cls(dataset_type, column) for column in columns] 

190 

191 

192def is_timespan_column(tag: ColumnTag) -> bool: 

193 """Test whether a column tag is a timespan. 

194 

195 Parameters 

196 ---------- 

197 tag : `ColumnTag` 

198 Column tag to test. 

199 

200 Returns 

201 ------- 

202 is_timespan : `bool` 

203 Whether the given column is a timespan. 

204 """ 

205 match tag: 

206 case DimensionRecordColumnTag(column="timespan"): 

207 return True 

208 case DatasetColumnTag(column="timespan"): 

209 return True 

210 return False