Source code for rainbow.agilent
import os
from rainbow.agilent import chemstation
from rainbow.datadirectory import DataDirectory
[docs]
def read(path, precision='auto', hrms=False, requested_files=None,
telemetry=False, centroid=False, bin_width=None):
"""
Reads an Agilent .D directory or .dx archive.
Args:
path (str): Path of the directory or .dx file.
precision (int or 'auto', optional): Number of decimals to round masses.
``'auto'`` picks 4 for high-resolution data and 0 otherwise, per file.
hrms (bool, optional): Flag for parsing the MassHunter profile
spectrum (MSProfile.bin).
requested_files (list, optional): List of filenames to parse.
telemetry (bool, optional): Flag for parsing .dx telemetry traces.
centroid (bool, optional): Flag for parsing the MassHunter centroid
spectrum (MSPeak.bin).
bin_width (float, optional): Shared-grid bin width in daltons for the
HRMS profile. Omit it (the default) to keep the per-scan
representation (one
:class:`~rainbow.agilent.masshunter.ProfileDataFile` per flight-time
grid); pass a width to project onto the shared m/z grid.
Returns:
DataDirectory representing the Agilent data.
"""
if os.path.splitext(path)[1].lower() == '.dx':
from rainbow.agilent import openlab
return openlab.read(path, precision, requested_files, telemetry)
datafiles = []
datafiles.extend(chemstation.parse_allfiles(path, precision, requested_files))
if hrms or centroid:
try:
from rainbow.agilent import masshunter
datafiles.extend(masshunter.parse_allfiles(
path, precision, hrms, centroid, bin_width))
except ModuleNotFoundError:
raise ModuleNotFoundError("You must install python-lzf to parse masshunter files.")
metadata = chemstation.parse_metadata(path, datafiles)
# Surface unread MassHunter profile/centroid data so the hrms and centroid
# flags are discoverable: a .D with MSProfile.bin holds a profile spectrum
# parsed only when hrms=True, and MSPeak.bin a centroid spectrum parsed only
# when centroid=True. Otherwise read() returns an empty DataDirectory with
# no hint that the flag is what's missing.
if not hrms and os.path.isfile(
os.path.join(path, "AcqData", "MSProfile.bin")):
metadata['hrms_available'] = True
if not centroid and os.path.isfile(
os.path.join(path, "AcqData", "MSPeak.bin")):
metadata['centroid_available'] = True
return DataDirectory(path, datafiles, metadata)
[docs]
def read_metadata(path):
"""
Reads metadata from an Agilent .D directory.
Args:
path (str): Path of the directory.
Returns:
Dictionary containing a list of datafiles and the metadata.
"""
if os.path.splitext(path)[1].lower() == '.dx':
from rainbow.agilent import openlab
return openlab.read_metadata(path)
datafiles = []
metadata = chemstation.parse_metadata(path, datafiles)
# MassHunter acquisitions (a .D with an AcqData subfolder) keep their data
# in MSProfile.bin (profile/HRMS) and/or MSPeak.bin (centroid), which the
# Chemstation .uv/.ch/.ms scan below never finds - so they used to come
# back with an empty datafile list. Mirror masshunter.parse_allfiles'
# detection to surface the datafile names and the flags needed to read
# them (hrms / centroid), without parsing the binaries.
acqdata_path = os.path.join(path, "AcqData")
if os.path.isdir(acqdata_path):
acqdata_files = set(os.listdir(acqdata_path))
if {"MSScan.xsd", "MSScan.bin"} <= acqdata_files:
mh_datafiles = []
if "MSProfile.bin" in acqdata_files:
mh_datafiles.append("MSProfile.bin")
metadata['hrms_available'] = True
if "MSPeak.bin" in acqdata_files:
mh_datafiles.append("MSPeak.bin")
metadata['centroid_available'] = True
if mh_datafiles:
return {'datafiles': mh_datafiles, 'metadata': metadata}
if len(metadata) == 1:
datadir = read(path)
if datadir:
return {'datafiles': datadir.datafiles + datadir.analog, 'metadata': datadir.metadata}
return None
# Masshunter datafiles are not located.
datafiles = [fn for fn in os.listdir(path) if fn[-3:].lower() in ('.uv', '.ch', '.ms')]
return {'datafiles': datafiles, 'metadata': metadata}