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 for info1, info2 in zip(self, other):
112 if info1 != info2:
113 return False
114 return True
116 def __setitem__(self, index, value):
117 """Store item in group.
119 Item must be an `ObservationInfo` or something that can be passed
120 to an `ObservationInfo` constructor.
121 """
122 value = self._coerce_value(value)
123 self._members[index] = value
124 self._sorted = None
126 def insert(self, index, value):
127 value = self._coerce_value(value)
128 self._members.insert(index, value)
129 self._sorted = None
131 def reverse(self):
132 self._members.reverse()
134 def sort(self, key=None, reverse=False):
135 self._members.sort(key=key, reverse=reverse)
136 if key is None and not reverse and self._sorted is None:
137 # Store sorted order in cache
138 self._sorted = self._members.copy()
140 def extremes(self):
141 """Return the oldest observation in the group and the newest.
143 If there is only one member of the group, the newest and oldest
144 can be the same observation.
146 Returns
147 -------
148 oldest : `ObservationInfo`
149 Oldest observation.
150 newest : `ObservationInfo`
151 Newest observation.
152 """
153 if self._sorted is None:
154 self._sorted = sorted(self._members)
155 return self._sorted[0], self._sorted[-1]
157 def newest(self):
158 """Return the newest observation in the group.
160 Returns
161 -------
162 newest : `ObservationInfo`
163 The newest `ObservationInfo` in the `ObservationGroup`.
164 """
165 return self.extremes()[1]
167 def oldest(self):
168 """Return the oldest observation in the group.
170 Returns
171 -------
172 oldest : `ObservationInfo`
173 The oldest `ObservationInfo` in the `ObservationGroup`.
174 """
175 return self.extremes()[0]
177 def property_values(self, property):
178 """Return a set of values associated with the specified property.
180 Parameters
181 ----------
182 property : `str`
183 Property of an `ObservationInfo`
185 Returns
186 -------
187 values : `set`
188 All the distinct values for that property within this group.
189 """
190 return {getattr(obs_info, property) for obs_info in self}