Coverage for python/astro_metadata_translator/observationGroup.py : 27%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of astro_metadata_translator.
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 LICENSE file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12"""Represent a collection of translated headers"""
14__all__ = ("ObservationGroup",)
16import logging
17from collections.abc import MutableSequence
19from .observationInfo import ObservationInfo
21log = logging.getLogger(__name__)
24class ObservationGroup(MutableSequence):
25 """A collection of `ObservationInfo` headers.
27 Parameters
28 ----------
29 members : iterable of `ObservationInfo` or `dict`-like
30 `ObservationInfo` to seed the group membership. If `dict`-like
31 values are used they will be passed to the `ObservationInfo`
32 constructor.
33 translator_class : `MetadataTranslator`-class, optional
34 If any of the members is not an `ObservationInfo`, translator class
35 to pass to the `ObservationInfo` constructor. If `None` the
36 translation class will be determined automatically.
37 pedantic : `bool`, optional
38 If any of the members is not an `ObservationInfo`, passed to the
39 `ObservationInfo` constructor to control whether
40 a failed translation is fatal or not. `None` indicates that the
41 `ObservationInfo` constructor default should be used.
42 """
44 def __init__(self, members, translator_class=None, pedantic=None):
45 self._members = [self._coerce_value(m, translator_class=translator_class, pedantic=pedantic)
46 for m in members]
48 # Cache of members in time order
49 self._sorted = None
51 def __len__(self):
52 return len(self._members)
54 def __delitem__(self, index):
55 del self._members[index]
56 self._sorted = None
58 def __getitem__(self, index):
59 return self._members[index]
61 def __str__(self):
62 results = []
63 for obs_info in self._members:
64 results.append(f"({obs_info.instrument}, {obs_info.datetime_begin})")
65 return "[" + ", ".join(results) + "]"
67 def _coerce_value(self, value, translator_class=None, pedantic=None):
68 """Given a value, ensure it is an `ObservationInfo`.
70 Parameters
71 ----------
72 value : `ObservationInfo` or `dict`-like
73 Either an `ObservationInfo` or something that can be passed to
74 an `ObservationInfo` constructor.
75 translator_class : `MetadataTranslator`-class, optional
76 If value is not an `ObservationInfo`, translator class to pass to
77 the `ObservationInfo` constructor. If `None` the
78 translation class will be determined automatically.
79 pedantic : `bool`, optional
80 If value is not an `ObservationInfo`, passed to the
81 `ObservationInfo` constructor to control whether
82 a failed translation is fatal or not. `None` indicates that the
83 `ObservationInfo` constructor default should be used.
85 Raises
86 ------
87 ValueError
88 Raised if supplied value is not an `ObservationInfo` and can
89 not be turned into one.
90 """
91 if value is None:
92 raise ValueError("An ObservationGroup cannot contain 'None'")
94 if not isinstance(value, ObservationInfo):
95 try:
96 kwargs = {"translator_class": translator_class}
97 if pedantic is not None:
98 kwargs["pedantic"] = pedantic
99 value = ObservationInfo(value, **kwargs)
100 except Exception as e:
101 raise ValueError("Could not convert value to ObservationInfo") from e
103 return value
105 def __iter__(self):
106 return iter(self._members)
108 def __eq__(self, other):
109 """Compares equal if all the members are equal in the same order.
110 """
111 if not isinstance(other, ObservationGroup):
112 return NotImplemented
114 for info1, info2 in zip(self, other):
115 if info1 != info2:
116 return False
117 return True
119 def __setitem__(self, index, value):
120 """Store item in group.
122 Item must be an `ObservationInfo` or something that can be passed
123 to an `ObservationInfo` constructor.
124 """
125 value = self._coerce_value(value)
126 self._members[index] = value
127 self._sorted = None
129 def insert(self, index, value):
130 value = self._coerce_value(value)
131 self._members.insert(index, value)
132 self._sorted = None
134 def reverse(self):
135 self._members.reverse()
137 def sort(self, key=None, reverse=False):
138 self._members.sort(key=key, reverse=reverse)
139 if key is None and not reverse and self._sorted is None:
140 # Store sorted order in cache
141 self._sorted = self._members.copy()
143 def extremes(self):
144 """Return the oldest observation in the group and the newest.
146 If there is only one member of the group, the newest and oldest
147 can be the same observation.
149 Returns
150 -------
151 oldest : `ObservationInfo`
152 Oldest observation.
153 newest : `ObservationInfo`
154 Newest observation.
155 """
156 if self._sorted is None:
157 self._sorted = sorted(self._members)
158 return self._sorted[0], self._sorted[-1]
160 def newest(self):
161 """Return the newest observation in the group.
163 Returns
164 -------
165 newest : `ObservationInfo`
166 The newest `ObservationInfo` in the `ObservationGroup`.
167 """
168 return self.extremes()[1]
170 def oldest(self):
171 """Return the oldest observation in the group.
173 Returns
174 -------
175 oldest : `ObservationInfo`
176 The oldest `ObservationInfo` in the `ObservationGroup`.
177 """
178 return self.extremes()[0]
180 def property_values(self, property):
181 """Return a set of values associated with the specified property.
183 Parameters
184 ----------
185 property : `str`
186 Property of an `ObservationInfo`
188 Returns
189 -------
190 values : `set`
191 All the distinct values for that property within this group.
192 """
193 return {getattr(obs_info, property) for obs_info in self}
195 def to_simple(self):
196 """Convert the group to simplified form.
198 Returns
199 -------
200 simple : `list` of `dict`
201 Simple form is a list containing the simplified dict form of
202 each `ObservationInfo`.
203 """
204 return [obsinfo.to_simple() for obsinfo in self]
206 @classmethod
207 def from_simple(cls, simple):
208 """Convert simplified form back to `ObservationGroup`
210 Parameters
211 ----------
212 simple : `list` of `dict`
213 Object returned by `to_simple`.
215 Returns
216 -------
217 group : `ObservationGroup`
218 Reconstructed group.
219 """
220 return cls((ObservationInfo.from_simple(o) for o in simple))