Repository: European-XFEL/EXtra-data

Issue:
Provide a means to access metadata fields
The new DAQ version records a few extra fields in the `METADATA` section of files, including start time and DAQ version. It should be possible to get this information through EXtra-data.

Complication: we allow combining data from more than one run. What should happen where the metadata differs?

Discussion:

Patch:
diff --git a/extra_data/file_access.py b/extra_data/file_access.py
index f70edb8..6ef8bea 100644
--- a/extra_data/file_access.py
+++ b/extra_data/file_access.py
@@ -292,6 +292,25 @@ class FileAccess:
             counts = np.uint64((ix_group['last'][:ntrains] - firsts + 1) * status)
         return firsts, counts
 
+    def metadata(self) -> dict:
+        """Get the contents of the METADATA group as a dict
+
+        Not including the lists of data sources
+        """
+        if self.format_version == '0.5':
+            # Pretend this is actually there, like format version 1.0
+            return {'dataFormatVersion': '0.5'}
+
+        r = {}
+        for k, ds in self.file['METADATA'].items():
+            if not isinstance(ds, h5py.Dataset):
+                continue
+            v = ds[0]
+            if isinstance(v, bytes):
+                v = v.decode('utf-8', 'surrogateescape')
+            r[k] = v
+        return r
+
     def get_keys(self, source):
         """Get keys for a given source name
 
diff --git a/extra_data/reader.py b/extra_data/reader.py
index e92934e..7d1c695 100644
--- a/extra_data/reader.py
+++ b/extra_data/reader.py
@@ -1220,6 +1220,18 @@ class DataCollection:
             return pd.Series(arr, index=self.train_ids)
         return arr
 
+    def run_metadata(self) -> dict:
+        """Get a dictionary of metadata about the run
+
+        From file format version 1.0, the files capture: creationDate,
+        daqLibrary, dataFormatVersion, karaboFramework, proposalNumber,
+        runNumber, sequenceNumber, updateDate.
+        """
+        if not self.is_single_run:
+            raise MultiRunError()
+
+        return self.files[0].metadata()
+
     def write(self, filename):
         """Write the selected data to a new HDF5 file
 


Test patch:
diff --git a/extra_data/tests/test_reader_mockdata.py b/extra_data/tests/test_reader_mockdata.py
index 3646df6..f629184 100644
--- a/extra_data/tests/test_reader_mockdata.py
+++ b/extra_data/tests/test_reader_mockdata.py
@@ -770,3 +770,18 @@ def test_get_run_values(mock_fxe_control_data):
     d = f.get_run_values(src, )
     assert isinstance(d['firmwareVersion.value'], np.int32)
     assert isinstance(d['enableShutter.value'], np.uint8)
+
+
+def test_run_metadata(mock_spb_raw_run):
+    run = RunDirectory(mock_spb_raw_run)
+    md = run.run_metadata()
+    if run.files[0].format_version == '0.5':
+        assert md == {'dataFormatVersion': '0.5'}
+    else:
+        assert md['dataFormatVersion'] == '1.0'
+        assert set(md) == {
+            'dataFormatVersion', 'creationDate', 'updateDate', 'daqLibrary',
+            'karaboFramework', 'proposalNumber', 'runNumber', 'runType',
+            'sample', 'sequenceNumber',
+        }
+        assert isinstance(md['creationDate'], str)