Coverage for python/lsst/daf/butler/core/_column_tags.py: 64%
72 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-07 02:05 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-07 02:05 -0800
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/>.
22from __future__ import annotations
24__all__ = (
25 "DatasetColumnTag",
26 "DimensionKeyColumnTag",
27 "DimensionRecordColumnTag",
28 "is_timespan_column",
29)
31import dataclasses
32from collections.abc import Iterable
33from typing import TYPE_CHECKING, Any, TypeVar, final
35_S = TypeVar("_S")
37if TYPE_CHECKING: 37 ↛ 38line 37 didn't jump to line 38, because the condition on line 37 was never true
38 from lsst.daf.relation import ColumnTag
41class _BaseColumnTag:
43 __slots__ = ()
45 @classmethod
46 def filter_from(cls: type[_S], tags: Iterable[Any]) -> set[_S]:
47 return {tag for tag in tags if type(tag) is cls}
50@final
51@dataclasses.dataclass(frozen=True, slots=True)
52class DimensionKeyColumnTag(_BaseColumnTag):
53 """An identifier for `~lsst.daf.relation.Relation` columns that represent
54 a dimension primary key value.
55 """
57 dimension: str
58 """Name of the dimension (`str`)."""
60 def __str__(self) -> str:
61 return self.dimension
63 @property
64 def qualified_name(self) -> str:
65 return self.dimension
67 @property
68 def is_key(self) -> bool:
69 return True
71 @classmethod
72 def generate(cls, dimensions: Iterable[str]) -> list[DimensionKeyColumnTag]:
73 """Return a list of column tags from an iterable of dimension
74 names.
76 Parameters
77 ----------
78 dimensions : `Iterable` [ `str` ]
79 Dimension names.
81 Returns
82 -------
83 tags : `list` [ `DimensionKeyColumnTag` ]
84 List of column tags.
85 """
86 return [cls(d) for d in dimensions]
89@final
90@dataclasses.dataclass(frozen=True, slots=True)
91class DimensionRecordColumnTag(_BaseColumnTag):
92 """An identifier for `~lsst.daf.relation.Relation` columns that represent
93 non-key columns in a dimension or dimension element record.
94 """
96 element: str
97 """Name of the dimension element (`str`).
98 """
100 column: str
101 """Name of the column (`str`)."""
103 def __str__(self) -> str:
104 return f"{self.element}.{self.column}"
106 @property
107 def qualified_name(self) -> str:
108 return f"n!{self.element}:{self.column}"
110 @property
111 def is_key(self) -> bool:
112 return False
114 @classmethod
115 def generate(cls, element: str, columns: Iterable[str]) -> list[DimensionRecordColumnTag]:
116 """Return a list of column tags from an iterable of column names
117 for a single dimension element.
119 Parameters
120 ----------
121 element : `str`
122 Name of the dimension element.
123 columns : `Iterable` [ `str` ]
124 Column names.
126 Returns
127 -------
128 tags : `list` [ `DimensionRecordColumnTag` ]
129 List of column tags.
130 """
131 return [cls(element, column) for column in columns]
134@final
135@dataclasses.dataclass(frozen=True, slots=True)
136class DatasetColumnTag(_BaseColumnTag):
137 """An identifier for `~lsst.daf.relation.Relation` columns that represent
138 columns from a dataset query or subquery.
139 """
141 dataset_type: str
142 """Name of the dataset type (`str`)."""
144 column: str
145 """Name of the column (`str`).
147 Allowed values are:
149 - "dataset_id" (autoincrement or UUID primary key)
150 - "run" (collection primary key, not collection name)
151 - "ingest_date"
152 - "timespan" (validity range, or NULL for non-calibration collections)
153 - "rank" (collection position in ordered search)
154 """
156 def __str__(self) -> str:
157 return f"{self.dataset_type}.{self.column}"
159 @property
160 def qualified_name(self) -> str:
161 return f"t!{self.dataset_type}:{self.column}"
163 @property
164 def is_key(self) -> bool:
165 return self.column == "dataset_id" or self.column == "run"
167 @classmethod
168 def generate(cls, dataset_type: str, columns: Iterable[str]) -> list[DatasetColumnTag]:
169 """Return a list of column tags from an iterable of column names
170 for a single dataset type.
172 Parameters
173 ----------
174 dataset_type : `str`
175 Name of the dataset type.
176 columns : `Iterable` [ `str` ]
177 Column names.
179 Returns
180 -------
181 tags : `list` [ `DatasetColumnTag` ]
182 List of column tags.
183 """
184 return [cls(dataset_type, column) for column in columns]
187def is_timespan_column(tag: ColumnTag) -> bool:
188 """Test whether a column tag is a timespan.
190 Parameters
191 ----------
192 tag : `ColumnTag`
193 Column tag to test.
195 Returns
196 -------
197 is_timespan : `bool`
198 Whether the given column is a timespan.
199 """
200 match tag:
201 case DimensionRecordColumnTag(column="timespan"):
202 return True
203 case DatasetColumnTag(column="timespan"):
204 return True
205 return False