Coverage for python/lsst/daf/butler/core/_column_tags.py: 64%
72 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03:05 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-22 03: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:
42 __slots__ = ()
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}
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 """
56 dimension: str
57 """Name of the dimension (`str`)."""
59 def __str__(self) -> str:
60 return self.dimension
62 @property
63 def qualified_name(self) -> str:
64 return self.dimension
66 @property
67 def is_key(self) -> bool:
68 return True
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.
75 Parameters
76 ----------
77 dimensions : `Iterable` [ `str` ]
78 Dimension names.
80 Returns
81 -------
82 tags : `list` [ `DimensionKeyColumnTag` ]
83 List of column tags.
84 """
85 return [cls(d) for d in dimensions]
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 """
95 element: str
96 """Name of the dimension element (`str`).
97 """
99 column: str
100 """Name of the column (`str`)."""
102 def __str__(self) -> str:
103 return f"{self.element}.{self.column}"
105 @property
106 def qualified_name(self) -> str:
107 return f"n!{self.element}:{self.column}"
109 @property
110 def is_key(self) -> bool:
111 return False
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.
118 Parameters
119 ----------
120 element : `str`
121 Name of the dimension element.
122 columns : `Iterable` [ `str` ]
123 Column names.
125 Returns
126 -------
127 tags : `list` [ `DimensionRecordColumnTag` ]
128 List of column tags.
129 """
130 return [cls(element, column) for column in columns]
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 """
140 dataset_type: str
141 """Name of the dataset type (`str`)."""
143 column: str
144 """Name of the column (`str`).
146 Allowed values are:
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 """
155 def __str__(self) -> str:
156 return f"{self.dataset_type}.{self.column}"
158 @property
159 def qualified_name(self) -> str:
160 return f"t!{self.dataset_type}:{self.column}"
162 @property
163 def is_key(self) -> bool:
164 return self.column == "dataset_id" or self.column == "run"
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.
171 Parameters
172 ----------
173 dataset_type : `str`
174 Name of the dataset type.
175 columns : `Iterable` [ `str` ]
176 Column names.
178 Returns
179 -------
180 tags : `list` [ `DatasetColumnTag` ]
181 List of column tags.
182 """
183 return [cls(dataset_type, column) for column in columns]
186def is_timespan_column(tag: ColumnTag) -> bool:
187 """Test whether a column tag is a timespan.
189 Parameters
190 ----------
191 tag : `ColumnTag`
192 Column tag to test.
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