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

70 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-01 02:05 -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 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 __future__ import annotations 

23 

24__all__ = ( 

25 "DatasetColumnTag", 

26 "DimensionKeyColumnTag", 

27 "DimensionRecordColumnTag", 

28 "is_timespan_column", 

29) 

30 

31import dataclasses 

32from collections.abc import Iterable 

33from typing import TYPE_CHECKING, Any, TypeVar, final 

34 

35_S = TypeVar("_S") 

36 

37if TYPE_CHECKING: 

38 from lsst.daf.relation import ColumnTag 

39 

40 

41class _BaseColumnTag: 

42 __slots__ = () 

43 

44 @classmethod 

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

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

47 

48 

49@final 

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

51class DimensionKeyColumnTag(_BaseColumnTag): 

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

53 a dimension primary key value. 

54 """ 

55 

56 dimension: str 

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

58 

59 def __str__(self) -> str: 

60 return self.dimension 

61 

62 @property 

63 def qualified_name(self) -> str: 

64 return self.dimension 

65 

66 @property 

67 def is_key(self) -> bool: 

68 return True 

69 

70 @classmethod 

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

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

73 names. 

74 

75 Parameters 

76 ---------- 

77 dimensions : `Iterable` [ `str` ] 

78 Dimension names. 

79 

80 Returns 

81 ------- 

82 tags : `list` [ `DimensionKeyColumnTag` ] 

83 List of column tags. 

84 """ 

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

86 

87 

88@final 

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

90class DimensionRecordColumnTag(_BaseColumnTag): 

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

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

93 """ 

94 

95 element: str 

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

97 """ 

98 

99 column: str 

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

101 

102 def __str__(self) -> str: 

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

104 

105 @property 

106 def qualified_name(self) -> str: 

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

108 

109 @property 

110 def is_key(self) -> bool: 

111 return False 

112 

113 @classmethod 

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

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

116 for a single dimension element. 

117 

118 Parameters 

119 ---------- 

120 element : `str` 

121 Name of the dimension element. 

122 columns : `Iterable` [ `str` ] 

123 Column names. 

124 

125 Returns 

126 ------- 

127 tags : `list` [ `DimensionRecordColumnTag` ] 

128 List of column tags. 

129 """ 

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

131 

132 

133@final 

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

135class DatasetColumnTag(_BaseColumnTag): 

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

137 columns from a dataset query or subquery. 

138 """ 

139 

140 dataset_type: str 

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

142 

143 column: str 

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

145 

146 Allowed values are: 

147 

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

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

150 - "ingest_date" 

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

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

153 """ 

154 

155 def __str__(self) -> str: 

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

157 

158 @property 

159 def qualified_name(self) -> str: 

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

161 

162 @property 

163 def is_key(self) -> bool: 

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

165 

166 @classmethod 

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

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

169 for a single dataset type. 

170 

171 Parameters 

172 ---------- 

173 dataset_type : `str` 

174 Name of the dataset type. 

175 columns : `Iterable` [ `str` ] 

176 Column names. 

177 

178 Returns 

179 ------- 

180 tags : `list` [ `DatasetColumnTag` ] 

181 List of column tags. 

182 """ 

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

184 

185 

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

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

188 

189 Parameters 

190 ---------- 

191 tag : `ColumnTag` 

192 Column tag to test. 

193 

194 Returns 

195 ------- 

196 is_timespan : `bool` 

197 Whether the given column is a timespan. 

198 """ 

199 match tag: 

200 case DimensionRecordColumnTag(column="timespan"): 

201 return True 

202 case DatasetColumnTag(column="timespan"): 

203 return True 

204 return False