evefile 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,320 @@
1
+ """
2
+
3
+ *Entities representing an eveH5 file on the entire file level.*
4
+
5
+ While the entities in this module represent the contents of an eveH5 file,
6
+ they clearly abstract from the internal structure of these files.
7
+ Furthermore, there are different versions of the underlying schema
8
+ (*i.e.*, organisation) of these files, and the entities abstract away from
9
+ these differences as well. The key concept is to provide users of the
10
+ ``evefile`` interface with useful abstractions allowing to conveniently
11
+ access all the data present in an eveH5 file.
12
+
13
+
14
+ Overview
15
+ ========
16
+
17
+ A first overview of the classes implemented in this module and their
18
+ hierarchy is given in the UML diagram below.
19
+
20
+
21
+ .. figure:: /uml/evefile.entities.file.*
22
+ :align: center
23
+
24
+ Class hierarchy of the :mod:`evefile.entities.file` module. The
25
+ :class:`File` class is sort of the central interface to the entire
26
+ subpackage, as this class provides a faithful representation of all
27
+ information available from a given eveH5 file. To this end,
28
+ it incorporates instances of classes of the other modules of the
29
+ subpackage. Furthermore, "Scan" inherits from the identically named
30
+ facade of the scan functional layer and contains the full information
31
+ of the SCML file (if the SCML file is present in the eveH5 file).
32
+
33
+
34
+ Module documentation
35
+ ====================
36
+
37
+ """
38
+
39
+ import datetime
40
+ import logging
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class File:
46
+ """
47
+ Representation of all information available from a given eveH5 file.
48
+
49
+ Individual measurements are saved in HDF5 files using a particular
50
+ schema (eveH5). Besides file-level metadata, there are log messages
51
+ and the actual data.
52
+
53
+ The data are organised in three functionally different sections: data,
54
+ snapshots, and monitors.
55
+
56
+
57
+ Attributes
58
+ ----------
59
+ metadata : :class:`Metadata`
60
+ File metadata
61
+
62
+ log_messages : :class:`list`
63
+ Log messages from an individual measurement
64
+
65
+ Each item in the list is an instance of :class:`LogMessage`.
66
+
67
+ data : :class:`dict`
68
+ Data recorded from the devices involved in the scan.
69
+
70
+ Each item is an instance of
71
+ :class:`evefile.entities.data.Data`.
72
+
73
+ snapshots : :class:`dict`
74
+ Device data recorded as snapshot during a measurement.
75
+
76
+ Each item is an instance of
77
+ :class:`evefile.entities.data.Data`.
78
+
79
+ monitors : :class:`dict`
80
+ Device data monitored during a measurement.
81
+
82
+ Each item is an instance of
83
+ :class:`evefile.entities.data.Data`.
84
+
85
+ position_timestamps : :class:`evefile.entities.data.TimestampData`
86
+ Timestamps for each individual position.
87
+
88
+ Monitors have timestamps (milliseconds since start of the scan)
89
+ rather than positions as primary quantisation axis. This object
90
+ provides a mapping between timestamps and positions and can be used
91
+ to map monitor data to positions.
92
+
93
+
94
+ Examples
95
+ --------
96
+ The :class:`File` class is not meant to be used directly, as any
97
+ entities, but rather indirectly by means of the respective facades in
98
+ the boundaries technical layer of the ``evefile`` package.
99
+ Hence, for the time being, there are no dedicated examples how to use
100
+ this class. Of course, you can instantiate an object as usual.
101
+
102
+ """
103
+
104
+ def __init__(self):
105
+ self.metadata = Metadata()
106
+ self.log_messages = []
107
+ self.data = {}
108
+ self.snapshots = {}
109
+ self.monitors = {}
110
+ self.position_timestamps = None
111
+
112
+
113
+ class Metadata:
114
+ """
115
+ Metadata of a given eveH5 file.
116
+
117
+ As measurements result in individual files, there is a series of
118
+ crucial metadata of such a measurement on this global level.
119
+
120
+
121
+ Attributes
122
+ ----------
123
+ filename : :class:`str`
124
+ Name (full path) of the eveH5 file.
125
+
126
+ eveh5_version : :class:`str`
127
+ Version of the eveH5 schema.
128
+
129
+ eve_version : :class:`str`
130
+ Version of the eve engine used to record the data.
131
+
132
+ xml_version : :class:`str`
133
+ Version of the schema used for the scan description (SCML/XML)
134
+
135
+ measurement_station : :class:`str`
136
+ Name of the measurement station used to record the data.
137
+
138
+ start : :class:`datetime.datetime`
139
+ Timestamp of the start of the measurement
140
+
141
+ end : :class:`datetime.datetime`
142
+ Timestamp of the end of the measurement
143
+
144
+ description : :class:`str`
145
+ User-entered description of the entire scan.
146
+
147
+ simulation : :class:`bool`
148
+ Flag signalling whether the measurement was a simulation.
149
+
150
+ Default: ``False``
151
+
152
+ preferred_axis : :class:`string`
153
+ Name of the axis marked as preferred in the scan description.
154
+
155
+ Default: ""
156
+
157
+ preferred_channel : :class:`string`
158
+ Name of the channel marked as preferred in the scan description.
159
+
160
+ Default: ""
161
+
162
+ preferred_normalisation_channel : :class:`string`
163
+ Name of the channel marked as preferred for normalising.
164
+
165
+ Default: ""
166
+
167
+ Examples
168
+ --------
169
+ The :class:`Metadata` class is not meant to be used directly, as any
170
+ entities, but rather indirectly by means of the respective facades in
171
+ the boundaries technical layer of the ``evefile`` package.
172
+ Hence, for the time being, there are no dedicated examples how to use
173
+ this class. Of course, you can instantiate an object as usual.
174
+
175
+ Nevertheless, as you may use the class indirectly, one important feature
176
+ should be highlighted here: the string representation used if you just
177
+ apply :func:`print` to an object of the class:
178
+
179
+ .. code-block::
180
+
181
+ print(Metadata())
182
+
183
+ The output of an (empty) object would look as follows:
184
+
185
+ .. code-block:: bash
186
+
187
+ filename:
188
+ eveh5_version:
189
+ eve_version:
190
+ xml_version:
191
+ measurement_station:
192
+ start: 2025-08-07 10:57:16.849298
193
+ end: 2025-08-07 10:57:16.849307
194
+ description:
195
+ simulation: False
196
+ preferred_axis:
197
+ preferred_channel:
198
+ preferred_normalisation_channel:
199
+
200
+ This can be used to get a convenient overview of the metadata contained
201
+ in a loaded eveH5 file.
202
+
203
+ """
204
+
205
+ def __init__(self):
206
+ self.filename = ""
207
+ self.eveh5_version = ""
208
+ self.eve_version = ""
209
+ self.xml_version = ""
210
+ self.measurement_station = ""
211
+ self.start = datetime.datetime.now()
212
+ self.end = datetime.datetime.now()
213
+ self.description = ""
214
+ self.simulation = False
215
+ self.preferred_axis = ""
216
+ self.preferred_channel = ""
217
+ self.preferred_normalisation_channel = ""
218
+
219
+ def __str__(self):
220
+ """
221
+ Human-readable representation of the metadata.
222
+
223
+ Returns
224
+ -------
225
+ output : :class:`str`
226
+ Multiline string with one attribute per line
227
+
228
+ """
229
+ output = []
230
+ # Note: Attributes are listed manually here for explicit ordering
231
+ attributes = [
232
+ "filename",
233
+ "eveh5_version",
234
+ "eve_version",
235
+ "xml_version",
236
+ "measurement_station",
237
+ "start",
238
+ "end",
239
+ "description",
240
+ "simulation",
241
+ "preferred_axis",
242
+ "preferred_channel",
243
+ "preferred_normalisation_channel",
244
+ ]
245
+ attribute_name_length = max(
246
+ len(attribute) for attribute in attributes
247
+ )
248
+ for attribute in attributes:
249
+ output.append(
250
+ f"{attribute:>{attribute_name_length}}:"
251
+ f" {getattr(self, attribute)}"
252
+ )
253
+ return "\n".join(output)
254
+
255
+
256
+ class LogMessage:
257
+ """
258
+ Log message from an individual measurement.
259
+
260
+ Operators can enter log messages during a measurement using the
261
+ eve-gui. In such case, the respective message appears in the eveH5
262
+ file together with a timestamp.
263
+
264
+
265
+ Attributes
266
+ ----------
267
+ timestamp : :class:`datetime.datetime`
268
+ Timestamp of the log message
269
+
270
+ message : :class:`str`
271
+ Actual content of the log message.
272
+
273
+
274
+ Examples
275
+ --------
276
+ The :class:`Scan` class is not meant to be used directly, as any
277
+ entities, but rather indirectly by means of the respective facades in
278
+ the boundaries technical layer of the ``evefile`` package.
279
+ Hence, for the time being, there are no dedicated examples how to use
280
+ this class. Of course, you can instantiate an object as usual.
281
+
282
+ """
283
+
284
+ def __init__(self):
285
+ self.timestamp = datetime.datetime.now()
286
+ self.message = ""
287
+
288
+ def from_string(self, string=""):
289
+ """
290
+ Set attributes from string.
291
+
292
+ In eveH5 files up to v7, the log messages are single strings with
293
+ the ISO timestamp at the beginning, followed by the actual message.
294
+ Timestamp and message are separated by ": ".
295
+
296
+ This method separates both parts and converts the timestamp into an
297
+ actual :obj:`datetime.datetime` object, consistent with the
298
+ :attr:`timestamp` attribute.
299
+
300
+ Parameters
301
+ ----------
302
+ string : :class:`str`
303
+ Log message consisting of timestamp and actual message.
304
+
305
+ """
306
+ timestamp, message = string.split(": ", maxsplit=1)
307
+ self.timestamp = datetime.datetime.fromisoformat(timestamp)
308
+ self.message = message
309
+
310
+ def __str__(self):
311
+ """
312
+ Human-readable representation of the log message.
313
+
314
+ Returns
315
+ -------
316
+ output : :class:`str`
317
+ String containing timestamp and log message
318
+
319
+ """
320
+ return f"{self.timestamp.isoformat()}: {self.message}"